From 8a1cb7c104627215b3a151d0152c9fe432fde5ab Mon Sep 17 00:00:00 2001 From: Felix Dittrich <31076102+f11h@users.noreply.github.com> Date: Thu, 19 Aug 2021 10:23:33 +0200 Subject: [PATCH] Feat: Add TeleTan Type (EVENT and TEST) (#239) * Add TeleTan Type (EVENT and TEST) * Fix Unit Tests * Changed Event Teletan Validity Co-authored-by: m.schulte --- .../config/VerificationApplicationConfig.java | 1 + .../controller/ExternalTanController.java | 14 +++--- .../controller/ExternalTokenController.java | 11 +++-- .../controller/InternalTanController.java | 41 ++++++++++++++-- .../domain/VerificationAppSession.java | 13 ++++- .../verification/domain/VerificationTan.java | 13 ++++- .../verification/model/AuthorizationRole.java | 3 +- .../verification/model/TeleTanType.java | 36 ++++++++++++++ .../service/AppSessionService.java | 5 +- .../verification/service/JwtService.java | 17 +++++-- .../verification/service/TanService.java | 30 ++++++++---- src/main/resources/application.yml | 1 + src/main/resources/db/changelog.yml | 3 ++ .../v002-add-teletan-type-column.yml | 19 ++++++++ .../coronawarn/verification/TestUtils.java | 7 ++- .../VerificationApplicationExternalTest.java | 9 ++-- .../VerificationApplicationInternalTest.java | 15 ++++-- .../verification/service/JwtServiceTest.java | 31 ++++++++---- .../verification/service/TanServiceTest.java | 48 ++++++++++++------- 19 files changed, 247 insertions(+), 70 deletions(-) create mode 100644 src/main/java/app/coronawarn/verification/model/TeleTanType.java create mode 100644 src/main/resources/db/changelog/v002-add-teletan-type-column.yml diff --git a/src/main/java/app/coronawarn/verification/config/VerificationApplicationConfig.java b/src/main/java/app/coronawarn/verification/config/VerificationApplicationConfig.java index 287182d3..b5637620 100644 --- a/src/main/java/app/coronawarn/verification/config/VerificationApplicationConfig.java +++ b/src/main/java/app/coronawarn/verification/config/VerificationApplicationConfig.java @@ -76,6 +76,7 @@ public static class Valid { private int length = 1; // Number of hours that teleTAN remains valid private int hours = 1; + private int eventDays = 2; } /** diff --git a/src/main/java/app/coronawarn/verification/controller/ExternalTanController.java b/src/main/java/app/coronawarn/verification/controller/ExternalTanController.java index a9dff6b4..59e9171d 100644 --- a/src/main/java/app/coronawarn/verification/controller/ExternalTanController.java +++ b/src/main/java/app/coronawarn/verification/controller/ExternalTanController.java @@ -83,7 +83,7 @@ public class ExternalTanController { * positive. * * @param registrationToken generated by a hashed guid or a teleTAN. {@link RegistrationToken} - * @param fake flag for fake request + * @param fake flag for fake request * @return A generated transaction number {@link Tan}. */ @Operation( @@ -134,17 +134,17 @@ public DeferredResult> generateTan(@Valid @RequestBody Regis } appSession.incrementTanCounter(); appSession.setUpdatedAt(LocalDateTime.now()); - + appSessionService.saveAppSession(appSession); - String generatedTan = tanService.generateVerificationTan(tanSourceOfTrust); + String generatedTan = tanService.generateVerificationTan(tanSourceOfTrust, appSession.getTeleTanType()); - Tan returnTan = generateReturnTan(generatedTan, fake); + Tan returnTan = generateReturnTan(generatedTan, fake); stopWatch.stop(); fakeDelayService.updateFakeTanRequestDelay(stopWatch.getTotalTimeMillis()); DeferredResult> deferredResult = new DeferredResult<>(); - scheduledExecutor.schedule(() -> deferredResult.setResult( - ResponseEntity.status(HttpStatus.CREATED).body(returnTan)), - fakeDelayService.realDelayTan(),MILLISECONDS); + scheduledExecutor.schedule(() -> deferredResult.setResult( + ResponseEntity.status(HttpStatus.CREATED).body(returnTan)), + fakeDelayService.realDelayTan(), MILLISECONDS); log.info("Returning the successfully generated tan."); return deferredResult; } diff --git a/src/main/java/app/coronawarn/verification/controller/ExternalTokenController.java b/src/main/java/app/coronawarn/verification/controller/ExternalTokenController.java index 50d14124..94c31a98 100644 --- a/src/main/java/app/coronawarn/verification/controller/ExternalTokenController.java +++ b/src/main/java/app/coronawarn/verification/controller/ExternalTokenController.java @@ -52,16 +52,12 @@ public class ExternalTokenController { private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4); - @NonNull private final FakeRequestService fakeRequestController; - @NonNull private final AppSessionService appSessionService; - @NonNull private final TanService tanService; - @NonNull private final FakeDelayService fakeDelayService; /** @@ -102,8 +98,13 @@ public DeferredResult> generateRegistrationTok log.info("Returning the successfully generated RegistrationToken."); return deferredResult; case TELETAN: - ResponseEntity response = appSessionService.generateRegistrationTokenByTeleTan(key, fake); Optional optional = tanService.getEntityByTan(key); + + ResponseEntity response = appSessionService.generateRegistrationTokenByTeleTan( + key, + fake, + optional.map(VerificationTan::getTeleTanType).orElse(null)); + if (optional.isPresent()) { VerificationTan teleTan = optional.get(); teleTan.setRedeemed(true); diff --git a/src/main/java/app/coronawarn/verification/controller/InternalTanController.java b/src/main/java/app/coronawarn/verification/controller/InternalTanController.java index 46bc353d..1f856b22 100644 --- a/src/main/java/app/coronawarn/verification/controller/InternalTanController.java +++ b/src/main/java/app/coronawarn/verification/controller/InternalTanController.java @@ -22,15 +22,20 @@ package app.coronawarn.verification.controller; import app.coronawarn.verification.exception.VerificationServerException; +import app.coronawarn.verification.model.AuthorizationRole; import app.coronawarn.verification.model.AuthorizationToken; import app.coronawarn.verification.model.Tan; import app.coronawarn.verification.model.TeleTan; +import app.coronawarn.verification.model.TeleTanType; import app.coronawarn.verification.service.JwtService; import app.coronawarn.verification.service.TanService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import javax.validation.Valid; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -66,6 +71,8 @@ public class InternalTanController { */ public static final String TELE_TAN_ROUTE = "/tan/teletan"; + public static final String TELE_TAN_TYPE_HEADER = "X-CWA-TELETAN-TYPE"; + @NonNull private final TanService tanService; @@ -83,7 +90,12 @@ public class InternalTanController { description = "The provided Tan is verified to be formerly issued by the verification server" ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Tan is valid an formerly issued by the verification server"), + @ApiResponse( + responseCode = "200", + description = "Tan is valid an formerly issued by the verification server", + headers = { + @Header(name = TELE_TAN_TYPE_HEADER, description = "Type of the TeleTan (TEST or EVENT)") + }), @ApiResponse(responseCode = "404", description = "Tan could not be verified")}) @PostMapping(value = TAN_VERIFY_ROUTE, consumes = MediaType.APPLICATION_JSON_VALUE @@ -96,7 +108,15 @@ public ResponseEntity verifyTan(@Valid @RequestBody Tan tan) { log.info("The Tan is valid."); return t; }) - .map(t -> ResponseEntity.ok().build()) + .map(t -> { + ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.ok(); + + if (t.getTeleTanType() != null) { + responseBuilder.header(TELE_TAN_TYPE_HEADER, t.getTeleTanType().toString()); + } + + return responseBuilder.build(); + }) .orElseGet(() -> { log.info("The Tan is invalid."); throw new VerificationServerException(HttpStatus.NOT_FOUND, "No Tan found or Tan is invalid"); @@ -119,10 +139,21 @@ public ResponseEntity verifyTan(@Valid @RequestBody Tan tan) { produces = MediaType.APPLICATION_JSON_VALUE ) public ResponseEntity createTeleTan( - @RequestHeader(JwtService.HEADER_NAME_AUTHORIZATION) @Valid AuthorizationToken authorization) { - if (jwtService.isAuthorized(authorization.getToken())) { + @RequestHeader(JwtService.HEADER_NAME_AUTHORIZATION) @Valid AuthorizationToken authorization, + @RequestHeader(value = TELE_TAN_TYPE_HEADER, required = false) @Valid TeleTanType teleTanType) { + + List requiredRoles = new ArrayList<>(); + + if (teleTanType == null) { + teleTanType = TeleTanType.TEST; + requiredRoles.add(AuthorizationRole.AUTH_C19_HOTLINE); + } else if (teleTanType == TeleTanType.EVENT) { + requiredRoles.add(AuthorizationRole.AUTH_C19_HOTLINE_EVENT); + } + + if (jwtService.isAuthorized(authorization.getToken(), requiredRoles)) { if (tanService.isTeleTanRateLimitNotExceeded()) { - String teleTan = tanService.generateVerificationTeleTan(); + String teleTan = tanService.generateVerificationTeleTan(teleTanType); log.info("The teleTAN is generated."); return ResponseEntity.status(HttpStatus.CREATED).body(new TeleTan(teleTan)); } else { diff --git a/src/main/java/app/coronawarn/verification/domain/VerificationAppSession.java b/src/main/java/app/coronawarn/verification/domain/VerificationAppSession.java index d7b61b3d..60517db1 100644 --- a/src/main/java/app/coronawarn/verification/domain/VerificationAppSession.java +++ b/src/main/java/app/coronawarn/verification/domain/VerificationAppSession.java @@ -22,6 +22,7 @@ package app.coronawarn.verification.domain; import app.coronawarn.verification.model.AppSessionSourceOfTrust; +import app.coronawarn.verification.model.TeleTanType; import java.io.Serializable; import java.time.LocalDateTime; import javax.persistence.Column; @@ -34,13 +35,17 @@ import javax.persistence.Table; import javax.persistence.Version; import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; /** * This class represents the AppSession-entity. */ -@Data +@Getter +@Setter +@EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor @Entity @@ -83,6 +88,10 @@ public class VerificationAppSession implements Serializable { @Enumerated(EnumType.STRING) private AppSessionSourceOfTrust sourceOfTrust; + @Column(name = "teletan_type") + @Enumerated(EnumType.STRING) + private TeleTanType teleTanType; + /** * This method increments the tan counter. */ diff --git a/src/main/java/app/coronawarn/verification/domain/VerificationTan.java b/src/main/java/app/coronawarn/verification/domain/VerificationTan.java index a88ef2cb..bc1283cc 100644 --- a/src/main/java/app/coronawarn/verification/domain/VerificationTan.java +++ b/src/main/java/app/coronawarn/verification/domain/VerificationTan.java @@ -23,6 +23,7 @@ import app.coronawarn.verification.model.TanSourceOfTrust; import app.coronawarn.verification.model.TanType; +import app.coronawarn.verification.model.TeleTanType; import java.io.Serializable; import java.time.LocalDateTime; import javax.persistence.Column; @@ -35,13 +36,17 @@ import javax.persistence.Table; import javax.persistence.Version; import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; /** * This class represents the TAN - entity. */ -@Data +@Getter +@Setter +@EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor @Entity @@ -85,6 +90,10 @@ public class VerificationTan implements Serializable { @Enumerated(EnumType.STRING) private TanType type; + @Column(name = "teletan_type") + @Enumerated(EnumType.STRING) + private TeleTanType teleTanType; + /** * Check if the tan can be redeemed by date. * diff --git a/src/main/java/app/coronawarn/verification/model/AuthorizationRole.java b/src/main/java/app/coronawarn/verification/model/AuthorizationRole.java index 171e375b..f2bb4026 100644 --- a/src/main/java/app/coronawarn/verification/model/AuthorizationRole.java +++ b/src/main/java/app/coronawarn/verification/model/AuthorizationRole.java @@ -33,7 +33,8 @@ @Getter public enum AuthorizationRole { AUTH_C19_HOTLINE("c19hotline"), - AUTH_C19_HEALTHAUTHORITY("c19healthauthority"); + AUTH_C19_HEALTHAUTHORITY("c19healthauthority"), + AUTH_C19_HOTLINE_EVENT("c19hotline_event"); private final String roleName; diff --git a/src/main/java/app/coronawarn/verification/model/TeleTanType.java b/src/main/java/app/coronawarn/verification/model/TeleTanType.java new file mode 100644 index 00000000..48e79dc0 --- /dev/null +++ b/src/main/java/app/coronawarn/verification/model/TeleTanType.java @@ -0,0 +1,36 @@ +/* + * Corona-Warn-App / cwa-verification + * + * (C) 2020, T-Systems International GmbH + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package app.coronawarn.verification.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema( + description = "The TeleTan Type model." +) +public enum TeleTanType { + + @Schema(description = "TeleTan is issued because of a positive PCR Test") + TEST, + + @Schema(description = "TeleTan is issued because of a confirmed infection at an event.") + EVENT +} diff --git a/src/main/java/app/coronawarn/verification/service/AppSessionService.java b/src/main/java/app/coronawarn/verification/service/AppSessionService.java index bd26ad6a..be912495 100644 --- a/src/main/java/app/coronawarn/verification/service/AppSessionService.java +++ b/src/main/java/app/coronawarn/verification/service/AppSessionService.java @@ -24,6 +24,7 @@ import app.coronawarn.verification.domain.VerificationAppSession; import app.coronawarn.verification.model.AppSessionSourceOfTrust; import app.coronawarn.verification.model.RegistrationToken; +import app.coronawarn.verification.model.TeleTanType; import app.coronawarn.verification.repository.VerificationAppSessionRepository; import java.time.LocalDateTime; import java.util.Optional; @@ -116,7 +117,8 @@ public ResponseEntity generateRegistrationTokenByGuid( * @param teleTan the TeleTan * @return an {@link ResponseEntity} */ - public ResponseEntity generateRegistrationTokenByTeleTan(String teleTan, String fake) { + public ResponseEntity generateRegistrationTokenByTeleTan( + String teleTan, String fake, TeleTanType teleTanType) { if (checkRegistrationTokenAlreadyExistForTeleTan(teleTan)) { log.warn("The registration token already exists for this TeleTAN."); return ResponseEntity.badRequest().build(); @@ -126,6 +128,7 @@ public ResponseEntity generateRegistrationTokenByTeleTan(Stri VerificationAppSession appSession = generateAppSession(registrationToken); appSession.setTeleTanHash(hashingService.hash(teleTan)); appSession.setSourceOfTrust(AppSessionSourceOfTrust.TELETAN); + appSession.setTeleTanType(teleTanType); saveAppSession(appSession); log.info("Returning the successfully created registration token."); return ResponseEntity.status(HttpStatus.CREATED).body( diff --git a/src/main/java/app/coronawarn/verification/service/JwtService.java b/src/main/java/app/coronawarn/verification/service/JwtService.java index 55be40ae..ac5b1ba2 100644 --- a/src/main/java/app/coronawarn/verification/service/JwtService.java +++ b/src/main/java/app/coronawarn/verification/service/JwtService.java @@ -83,16 +83,17 @@ public class JwtService { * token is valid. * * @param authorizationToken The authorization token to validate + * @param mandatoryRoles list of roles which are required to pass * @return true, if the token is valid, otherwise false */ - public boolean isAuthorized(String authorizationToken) { + public boolean isAuthorized(String authorizationToken, List mandatoryRoles) { // check if the JWT is enabled if (!verificationApplicationConfig.getJwt().getEnabled()) { return true; } if (null != authorizationToken && authorizationToken.startsWith(TOKEN_PREFIX)) { String jwtToken = authorizationToken.substring(TOKEN_PREFIX.length()); - return validateToken(jwtToken, getPublicKey()); + return validateToken(jwtToken, getPublicKey(), mandatoryRoles); } return false; } @@ -102,13 +103,23 @@ public boolean isAuthorized(String authorizationToken) { * * @param token The authorization token to validate * @param publicKey the key from the IAM server + * @param mandatoryRoles List of roles which are required to pass. * @return true, if the token is valid, otherwise false */ - public boolean validateToken(final String token, final PublicKey publicKey) { + public boolean validateToken(final String token, final PublicKey publicKey, List mandatoryRoles) { log.debug("process validateToken() by - token: {} PK: {}", token, publicKey); if (null != publicKey) { try { List roleNames = getRoles(token, publicKey); + + // Return false if one of the mandatory roles are not present + for (AuthorizationRole mandatoryRole : mandatoryRoles) { + if (!roleNames.contains(mandatoryRole.getRoleName())) { + return false; + } + } + + // Return true if at least one of the authorization roles are present AuthorizationRole[] roles = AuthorizationRole.values(); for (AuthorizationRole role : roles) { if (roleNames.contains(role.getRoleName())) { diff --git a/src/main/java/app/coronawarn/verification/service/TanService.java b/src/main/java/app/coronawarn/verification/service/TanService.java index 6f89bafc..2496e912 100644 --- a/src/main/java/app/coronawarn/verification/service/TanService.java +++ b/src/main/java/app/coronawarn/verification/service/TanService.java @@ -25,6 +25,7 @@ import app.coronawarn.verification.domain.VerificationTan; import app.coronawarn.verification.model.TanSourceOfTrust; import app.coronawarn.verification.model.TanType; +import app.coronawarn.verification.model.TeleTanType; import app.coronawarn.verification.repository.VerificationTanRepository; import java.security.SecureRandom; import java.time.LocalDateTime; @@ -35,6 +36,7 @@ import java.util.regex.Pattern; import java.util.stream.Collector; import java.util.stream.IntStream; +import javax.validation.constraints.NotNull; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -157,10 +159,12 @@ protected String generateValidTan(Supplier tanCreator) { * * @param tan the TAN * @param tanType the TAN type + * @param teleTanType type of the teleTan * @return the persisted TAN */ - private VerificationTan persistTan(String tan, TanType tanType, TanSourceOfTrust sourceOfTrust) { - VerificationTan newTan = generateVerificationTan(tan, tanType, sourceOfTrust); + private VerificationTan persistTan( + String tan, TanType tanType, TanSourceOfTrust sourceOfTrust, TeleTanType teleTanType) { + VerificationTan newTan = generateVerificationTan(tan, tanType, sourceOfTrust, teleTanType); return tanRepository.save(newTan); } @@ -219,9 +223,9 @@ public boolean checkTanNotExist(String tan) { * * @return a valid teleTAN */ - public String generateVerificationTeleTan() { + public String generateVerificationTeleTan(@NotNull TeleTanType teleTanType) { String teleTan = generateValidTan(this::createTeleTan); - persistTan(teleTan, TanType.TELETAN, TanSourceOfTrust.TELETAN); + persistTan(teleTan, TanType.TELETAN, TanSourceOfTrust.TELETAN, teleTanType); return teleTan; } @@ -229,11 +233,12 @@ public String generateVerificationTeleTan() { * This Method generates a valid TAN and persists it. Returns the generated TAN. * * @param sourceOfTrust sets the source of Trust for the Tan + * @param teleTanType type of the teleTan * @return a valid tan with given source of Trust */ - public String generateVerificationTan(TanSourceOfTrust sourceOfTrust) { + public String generateVerificationTan(TanSourceOfTrust sourceOfTrust, TeleTanType teleTanType) { String tan = generateValidTan(this::createTanFromUuid); - persistTan(tan, TanType.TAN, sourceOfTrust); + persistTan(tan, TanType.TAN, sourceOfTrust, teleTanType); return tan; } @@ -244,13 +249,21 @@ public String generateVerificationTan(TanSourceOfTrust sourceOfTrust) { * @param sourceOfTrust source of trust of the tan * @return Tan object */ - public VerificationTan generateVerificationTan(String tan, TanType tanType, TanSourceOfTrust sourceOfTrust) { + public VerificationTan generateVerificationTan( + String tan, TanType tanType, TanSourceOfTrust sourceOfTrust, TeleTanType teleTanType) { + LocalDateTime from = LocalDateTime.now(); LocalDateTime until; int tanValidInDays = verificationApplicationConfig.getTan().getValid().getDays(); int teleTanValidInHours = verificationApplicationConfig.getTan().getTele().getValid().getHours(); + int eventTeleTanValidInDays = verificationApplicationConfig.getTan().getTele().getValid().getEventDays(); + if (tanType == TanType.TELETAN) { - until = from.plusHours(teleTanValidInHours); + if (teleTanType == TeleTanType.TEST) { + until = from.plusHours(teleTanValidInHours); + } else { + until = from.plusDays(eventTeleTanValidInDays); + } } else { until = from.plusDays(tanValidInDays); } @@ -264,6 +277,7 @@ public VerificationTan generateVerificationTan(String tan, TanType tanType, TanS verificationTan.setCreatedAt(LocalDateTime.now()); verificationTan.setUpdatedAt(LocalDateTime.now()); verificationTan.setType(tanType); + verificationTan.setTeleTanType(teleTanType); return verificationTan; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e303c930..d4598d82 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -67,6 +67,7 @@ tan: valid: length: 9 hours: 1 + eventDays: 2 valid: days: 14 appsession: diff --git a/src/main/resources/db/changelog.yml b/src/main/resources/db/changelog.yml index 151e332f..81b4c1ff 100644 --- a/src/main/resources/db/changelog.yml +++ b/src/main/resources/db/changelog.yml @@ -8,3 +8,6 @@ databaseChangeLog: - include: file: changelog/v001-add-dob-hash-column.yml relativeToChangelogFile: true + - include: + file: changelog/v002-add-teletan-type-column.yml + relativeToChangelogFile: true diff --git a/src/main/resources/db/changelog/v002-add-teletan-type-column.yml b/src/main/resources/db/changelog/v002-add-teletan-type-column.yml new file mode 100644 index 00000000..abaf1aa1 --- /dev/null +++ b/src/main/resources/db/changelog/v002-add-teletan-type-column.yml @@ -0,0 +1,19 @@ +databaseChangeLog: +- changeSet: + id: add-teletan-type-column + author: f11h + changes: + - addColumn: + tableName: tan + columns: + name: teletan_type + type: varchar(10) + constraints: + nullable: true + - addColumn: + tableName: app_session + columns: + name: teletan_type + type: varchar(10) + constraints: + nullable: true diff --git a/src/test/java/app/coronawarn/verification/TestUtils.java b/src/test/java/app/coronawarn/verification/TestUtils.java index 145a5661..2805c46f 100644 --- a/src/test/java/app/coronawarn/verification/TestUtils.java +++ b/src/test/java/app/coronawarn/verification/TestUtils.java @@ -6,13 +6,13 @@ import app.coronawarn.verification.model.AuthorizationRole; import app.coronawarn.verification.model.TanSourceOfTrust; import app.coronawarn.verification.model.TanType; +import app.coronawarn.verification.model.TeleTanType; import app.coronawarn.verification.model.TestResult; import app.coronawarn.verification.repository.VerificationAppSessionRepository; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; -import java.io.UnsupportedEncodingException; import java.security.PrivateKey; import java.time.Instant; import java.time.LocalDateTime; @@ -76,6 +76,7 @@ static void prepareAppSessionTestDataSotTeleTan(VerificationAppSessionRepository static VerificationAppSession getAppSessionTestData(AppSessionSourceOfTrust sot, boolean dob) { VerificationAppSession cv = new VerificationAppSession(); + cv.setTeleTanType(TeleTanType.EVENT); cv.setHashedGuid(TEST_GUI_HASH); cv.setHashedGuidDob(dob ? TEST_GUI_HASH_DOB : null); cv.setCreatedAt(LocalDateTime.now()); @@ -92,6 +93,7 @@ static VerificationAppSession getAppSessionTestData() { static VerificationTan getTeleTanTestData() { VerificationTan cvtan = new VerificationTan(); + cvtan.setTeleTanType(TeleTanType.EVENT); cvtan.setCreatedAt(LocalDateTime.now()); cvtan.setUpdatedAt(LocalDateTime.now()); cvtan.setRedeemed(false); @@ -105,6 +107,7 @@ static VerificationTan getTeleTanTestData() { static VerificationTan getVerificationTANTestData() { VerificationTan cvtan = new VerificationTan(); + cvtan.setTeleTanType(TeleTanType.EVENT); cvtan.setCreatedAt(LocalDateTime.now()); cvtan.setUpdatedAt(LocalDateTime.now()); cvtan.setRedeemed(false); @@ -120,7 +123,7 @@ static String getAsJsonFormat(Object o) throws JsonProcessingException { return objectMapper.writeValueAsString(o); } - static String getJwtTestData(final long expirationSecondsToAdd, PrivateKey privateKey, AuthorizationRole... roles) throws UnsupportedEncodingException { + static String getJwtTestData(final long expirationSecondsToAdd, PrivateKey privateKey, AuthorizationRole... roles) { final Map> realmAccessMap = new HashMap<>(); final List roleNames = new ArrayList<>(); for (AuthorizationRole role : roles) { diff --git a/src/test/java/app/coronawarn/verification/VerificationApplicationExternalTest.java b/src/test/java/app/coronawarn/verification/VerificationApplicationExternalTest.java index 9d47f278..bcadee39 100644 --- a/src/test/java/app/coronawarn/verification/VerificationApplicationExternalTest.java +++ b/src/test/java/app/coronawarn/verification/VerificationApplicationExternalTest.java @@ -41,6 +41,7 @@ import app.coronawarn.verification.model.RegistrationToken; import app.coronawarn.verification.model.RegistrationTokenKeyType; import app.coronawarn.verification.model.RegistrationTokenRequest; +import app.coronawarn.verification.model.TeleTanType; import app.coronawarn.verification.repository.VerificationAppSessionRepository; import app.coronawarn.verification.service.TanService; import app.coronawarn.verification.service.TestResultServerService; @@ -132,7 +133,7 @@ public void callGenerateTan() throws Exception { TestUtils.prepareAppSessionTestData(appSessionrepository); doReturn(TestUtils.TEST_LAB_POSITIVE_RESULT).when(testResultServerService).result(any()); - doReturn(TestUtils.TEST_TAN).when(tanService).generateVerificationTan(any()); + doReturn(TestUtils.TEST_TAN).when(tanService).generateVerificationTan(any(), any()); MvcResult result = mockMvc.perform(post(TestUtils.PREFIX_API_VERSION + "/tan") .secure(true) @@ -153,6 +154,7 @@ public void callGenerateTan() throws Exception { assertEquals(TestUtils.TEST_GUI_HASH, verificationList.get(0).getHashedGuid()); assertEquals(AppSessionSourceOfTrust.HASHED_GUID, verificationList.get(0).getSourceOfTrust()); assertEquals(TestUtils.TEST_REG_TOK_HASH, verificationList.get(0).getRegistrationTokenHash()); + assertEquals(TeleTanType.EVENT, verificationList.get(0).getTeleTanType()); } @@ -162,7 +164,7 @@ public void callGenerateTanForQuickTest() throws Exception { TestUtils.prepareAppSessionTestData(appSessionrepository); doReturn(TestUtils.QUICK_TEST_POSITIVE_RESULT).when(testResultServerService).result(any()); - doReturn(TestUtils.TEST_TAN).when(tanService).generateVerificationTan(any()); + doReturn(TestUtils.TEST_TAN).when(tanService).generateVerificationTan(any(), any()); MvcResult result = mockMvc.perform(post(TestUtils.PREFIX_API_VERSION + "/tan") .secure(true) @@ -309,7 +311,7 @@ public void callGenerateTanWithTeleTanAppSession() throws Exception { appSessionTestData.setSourceOfTrust(AppSessionSourceOfTrust.TELETAN); appSessionrepository.save(appSessionTestData); doReturn(TestUtils.TEST_LAB_NEGATIVE_RESULT).when(testResultServerService).result(any()); - doReturn(TestUtils.TEST_TELE_TAN).when(tanService).generateVerificationTan(any()); + doReturn(TestUtils.TEST_TELE_TAN).when(tanService).generateVerificationTan(any(), any()); MvcResult result = mockMvc.perform(post(TestUtils.PREFIX_API_VERSION + "/tan") .secure(true) @@ -587,6 +589,7 @@ public void callGetRegistrationTokenByTeleTan() throws Exception { assertNull(verificationList.get(0).getHashedGuid()); assertEquals(AppSessionSourceOfTrust.TELETAN, verificationList.get(0).getSourceOfTrust()); assertNotNull(verificationList.get(0).getRegistrationTokenHash()); + assertEquals(TeleTanType.EVENT, verificationList.get(0).getTeleTanType()); } /** diff --git a/src/test/java/app/coronawarn/verification/VerificationApplicationInternalTest.java b/src/test/java/app/coronawarn/verification/VerificationApplicationInternalTest.java index 576ab1ae..db8a72d9 100644 --- a/src/test/java/app/coronawarn/verification/VerificationApplicationInternalTest.java +++ b/src/test/java/app/coronawarn/verification/VerificationApplicationInternalTest.java @@ -28,14 +28,17 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import app.coronawarn.verification.controller.InternalTanController; import app.coronawarn.verification.domain.VerificationTan; import app.coronawarn.verification.model.AuthorizationRole; import app.coronawarn.verification.model.HashedGuid; import app.coronawarn.verification.model.RegistrationToken; import app.coronawarn.verification.model.Tan; +import app.coronawarn.verification.model.TeleTanType; import app.coronawarn.verification.repository.VerificationAppSessionRepository; import app.coronawarn.verification.service.JwtService; import app.coronawarn.verification.service.TanService; @@ -43,6 +46,7 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.time.LocalDateTime; +import java.util.Collections; import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; @@ -98,9 +102,9 @@ public void callGenerateTeleTAN() throws Exception { String jwtString = TestUtils.getJwtTestData(3000, kp.getPrivate(), AuthorizationRole.AUTH_C19_HEALTHAUTHORITY); given(this.tanService.isTeleTanRateLimitNotExceeded()).willReturn(Boolean.TRUE); - given(this.jwtService.isAuthorized(any())).willReturn(Boolean.TRUE); + given(this.jwtService.isAuthorized(any(), any())).willReturn(Boolean.TRUE); given(this.jwtService.getPublicKey()).willReturn(kp.getPublic()); - when(this.jwtService.validateToken(jwtString, kp.getPublic())).thenCallRealMethod(); + when(this.jwtService.validateToken(jwtString, kp.getPublic(), Collections.emptyList())).thenCallRealMethod(); mockMvc.perform(post(TestUtils.PREFIX_API_VERSION + "/tan/teletan").header(JwtService.HEADER_NAME_AUTHORIZATION, JwtService.TOKEN_PREFIX + jwtString).secure(true)) .andExpect(status().isCreated()); @@ -118,7 +122,7 @@ public void callGenerateTeleTanUnauthorized() throws Exception { KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA"); keyGenerator.initialize(2048); KeyPair kp = keyGenerator.genKeyPair(); - given(this.jwtService.isAuthorized(any())).willReturn(false); + given(this.jwtService.isAuthorized(any(), any())).willReturn(false); given(this.jwtService.getPublicKey()).willReturn(kp.getPublic()); String jwtString = TestUtils.getJwtTestData(3000, kp.getPrivate(), AuthorizationRole.AUTH_C19_HEALTHAUTHORITY); mockMvc.perform(post(TestUtils.PREFIX_API_VERSION + "/tan/teletan").header(JwtService.HEADER_NAME_AUTHORIZATION, @@ -144,7 +148,8 @@ public void callVerifyTAN() throws Exception { .contentType(MediaType.APPLICATION_JSON) .secure(true) .content(TestUtils.getAsJsonFormat(new Tan(TestUtils.TEST_TAN, TAN_PADDING)))) - .andExpect(status().isOk()); + .andExpect(status().isOk()) + .andExpect(header().string(InternalTanController.TELE_TAN_TYPE_HEADER, TeleTanType.EVENT.toString())); } /** @@ -265,7 +270,7 @@ public void callGetRegistrationTokenByGuid() throws Exception { @Test public void shouldReturn429StatusCodeIfRateLimitIsExceeded() throws Exception { - given(this.jwtService.isAuthorized(any())).willReturn(Boolean.TRUE); + given(this.jwtService.isAuthorized(any(), any())).willReturn(Boolean.TRUE); given(this.tanService.isTeleTanRateLimitNotExceeded()).willReturn(Boolean.TRUE); mockMvc.perform(post(TestUtils.PREFIX_API_VERSION + "/tan/teletan").header(JwtService.HEADER_NAME_AUTHORIZATION, "").secure(true)) diff --git a/src/test/java/app/coronawarn/verification/service/JwtServiceTest.java b/src/test/java/app/coronawarn/verification/service/JwtServiceTest.java index 3a938f2a..14d8a460 100644 --- a/src/test/java/app/coronawarn/verification/service/JwtServiceTest.java +++ b/src/test/java/app/coronawarn/verification/service/JwtServiceTest.java @@ -67,10 +67,16 @@ public class JwtServiceTest { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } - private JwtService jwtService = new JwtService(new IamClientMock(), new VerificationApplicationConfig()); + private final VerificationApplicationConfig config = new VerificationApplicationConfig(); + + private JwtService jwtService = new JwtService(new IamClientMock(), config); @BeforeEach public void setUp() throws NoSuchAlgorithmException { + VerificationApplicationConfig.Jwt jwtConfig = new VerificationApplicationConfig.Jwt(); + jwtConfig.setEnabled(true); + config.setJwt(jwtConfig); + KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(RSA); keyGenerator.initialize(2048); KeyPair kp = keyGenerator.genKeyPair(); @@ -85,7 +91,7 @@ public void setUp() throws NoSuchAlgorithmException { /** * Test to validate an valid Token, with the - * {@link JwtService#validateToken(java.lang.String, java.security.PublicKey)} method. + * {@link JwtService#validateToken(String, PublicKey, List)} method. * * @throws java.io.UnsupportedEncodingException if the test cannot be performed. * @throws java.security.NoSuchAlgorithmException if the test cannot be performed. @@ -93,12 +99,15 @@ public void setUp() throws NoSuchAlgorithmException { @Test public void validateToken() throws UnsupportedEncodingException, NoSuchAlgorithmException { String jwToken = getJwtTestData(3000, AuthorizationRole.AUTH_C19_HOTLINE, AuthorizationRole.AUTH_C19_HEALTHAUTHORITY); - Assertions.assertTrue(jwtService.validateToken(jwToken, publicKey)); + Assertions.assertTrue(jwtService.validateToken(jwToken, publicKey, Collections.emptyList())); + + jwToken = getJwtTestData(3000, AuthorizationRole.AUTH_C19_HOTLINE, AuthorizationRole.AUTH_C19_HEALTHAUTHORITY); + Assertions.assertFalse(jwtService.validateToken(jwToken, publicKey, List.of(AuthorizationRole.AUTH_C19_HOTLINE_EVENT))); } /** * Test the negative case by not given public key, with the - * {@link JwtService#validateToken(java.lang.String, java.security.PublicKey)} method. + * {@link JwtService#validateToken(String, PublicKey, List)} method. * * @throws java.io.UnsupportedEncodingException if the test cannot be performed. * @throws java.security.NoSuchAlgorithmException if the test cannot be performed. @@ -106,11 +115,11 @@ public void validateToken() throws UnsupportedEncodingException, NoSuchAlgorithm @Test public void validateTokenByPublicKeyIsNull() throws UnsupportedEncodingException, NoSuchAlgorithmException { String jwToken = getJwtTestData(3000, AuthorizationRole.AUTH_C19_HOTLINE, AuthorizationRole.AUTH_C19_HEALTHAUTHORITY); - Assertions.assertFalse(jwtService.validateToken(jwToken, null)); + Assertions.assertFalse(jwtService.validateToken(jwToken, null, Collections.emptyList())); } /** - * Test is Token authorized, with the {@link JwtService#isAuthorized(java.lang.String)} method. + * Test is Token authorized, with the {@link JwtService#isAuthorized(String, List)} method. * * @throws java.io.UnsupportedEncodingException if the test cannot be performed. * @throws java.security.NoSuchAlgorithmException if the test cannot be performed. @@ -119,13 +128,15 @@ public void validateTokenByPublicKeyIsNull() throws UnsupportedEncodingException public void tokenIsAuthorized() throws UnsupportedEncodingException, NoSuchAlgorithmException { String jwToken = getJwtTestData(3000, AuthorizationRole.AUTH_C19_HOTLINE, AuthorizationRole.AUTH_C19_HEALTHAUTHORITY); IamClientMock clientMock = createIamClientMock(); - jwtService = new JwtService(clientMock, new VerificationApplicationConfig()); - Assertions.assertTrue(jwtService.isAuthorized(TOKEN_PREFIX + jwToken)); + jwtService = new JwtService(clientMock, config); + Assertions.assertTrue(jwtService.isAuthorized(TOKEN_PREFIX + jwToken, Collections.emptyList())); + + Assertions.assertFalse(jwtService.isAuthorized(TOKEN_PREFIX + getJwtTestData(3000, AuthorizationRole.AUTH_C19_HOTLINE), List.of(AuthorizationRole.AUTH_C19_HOTLINE_EVENT))); } /** * Test to validate an expired Token, with the - * {@link JwtService#validateToken(java.lang.String, java.security.PublicKey)} method. + * {@link JwtService#validateToken(String, PublicKey, List)} method. * * @throws java.io.UnsupportedEncodingException if the test cannot be performed. * @throws java.security.NoSuchAlgorithmException if the test cannot be performed. @@ -133,7 +144,7 @@ public void tokenIsAuthorized() throws UnsupportedEncodingException, NoSuchAlgor @Test public void validateExpiredToken() throws UnsupportedEncodingException, NoSuchAlgorithmException { String jwToken = getJwtTestData(0, AuthorizationRole.AUTH_C19_HOTLINE, AuthorizationRole.AUTH_C19_HEALTHAUTHORITY); - Assertions.assertFalse(jwtService.validateToken(jwToken, publicKey)); + Assertions.assertFalse(jwtService.validateToken(jwToken, publicKey, Collections.emptyList())); } private String getJwtTestData(final long expirationSecondsToAdd, AuthorizationRole... roles) throws UnsupportedEncodingException, NoSuchAlgorithmException { diff --git a/src/test/java/app/coronawarn/verification/service/TanServiceTest.java b/src/test/java/app/coronawarn/verification/service/TanServiceTest.java index 9850fbab..1fa1be35 100644 --- a/src/test/java/app/coronawarn/verification/service/TanServiceTest.java +++ b/src/test/java/app/coronawarn/verification/service/TanServiceTest.java @@ -26,6 +26,7 @@ import app.coronawarn.verification.domain.VerificationTan; import app.coronawarn.verification.model.TanSourceOfTrust; import app.coronawarn.verification.model.TanType; +import app.coronawarn.verification.model.TeleTanType; import app.coronawarn.verification.repository.VerificationTanRepository; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; @@ -107,7 +108,7 @@ public void deleteTan() { tanService.saveTan(tan); Optional tanFromDB = tanService.getEntityByTan(TEST_TAN); - Assertions.assertEquals(tan, tanFromDB.get()); + Assertions.assertEquals(tan, tanFromDB.orElseThrow()); tanService.deleteTan(tan); tanFromDB = tanService.getEntityByTan(TEST_TAN); assertFalse(tanFromDB.isPresent()); @@ -126,6 +127,7 @@ public void saveTan() { tan.setValidFrom(LocalDateTime.now()); tan.setValidUntil(TAN_VALID_UNTIL_IN_DAYS); tan.setType(TEST_TAN_TYPE); + tan.setTeleTanType(TeleTanType.TEST); tan.setSourceOfTrust(TEST_TELE_TAN_SOURCE_OF_TRUST); VerificationTan retunedTan = tanService.saveTan(tan); Assertions.assertEquals(retunedTan, tan); @@ -144,10 +146,11 @@ public void getEntityByTan() { tan.setValidUntil(LocalDateTime.parse((TAN_VALID_UNTIL_IN_DAYS.format(FORMATTER)))); tan.setType(TEST_TAN_TYPE); tan.setSourceOfTrust(TEST_TELE_TAN_SOURCE_OF_TRUST); + tan.setTeleTanType(TeleTanType.TEST); tanService.saveTan(tan); Optional tanFromDB = tanService.getEntityByTan(TEST_TAN); - Assertions.assertEquals(tan, tanFromDB.get()); + Assertions.assertEquals(tan, tanFromDB.orElseThrow()); } @Test @@ -161,6 +164,7 @@ public void checkTanAlreadyExist() { tan.setValidFrom(start); tan.setValidUntil(LocalDateTime.parse((TELE_TAN_VALID_UNTIL_IN_HOURS.format(FORMATTER)))); tan.setType(TanType.TELETAN); + tan.setTeleTanType(TeleTanType.TEST); tan.setSourceOfTrust(TEST_TELE_TAN_SOURCE_OF_TRUST); tanService.saveTan(tan); assertFalse(tanService.checkTanNotExist(TEST_TELE_TAN)); @@ -168,7 +172,7 @@ public void checkTanAlreadyExist() { @Test public void generateVerificationTan() { - String tan = tanService.generateVerificationTan(TEST_TELE_TAN_SOURCE_OF_TRUST); + String tan = tanService.generateVerificationTan(TEST_TELE_TAN_SOURCE_OF_TRUST, null); assertTrue(syntaxTanVerification(tan)); assertFalse(tan.isEmpty()); } @@ -189,16 +193,15 @@ public void createTeleTan() { @Test public void verifyTeletan() { - String teleTan = tanService.generateVerificationTeleTan(); + String teleTan = tanService.generateVerificationTeleTan(TeleTanType.TEST); assertTrue(tanService.checkTanNotExist(TEST_TELE_TAN)); - boolean value = tanService.verifyTeleTan(teleTan); - assertTrue(value); + assertTrue(tanService.verifyTeleTan(teleTan)); assertFalse(tanService.verifyTeleTan("R3ZNUI0")); } @Test public void verifyAlreadyRedeemedTeleTan() { - String teleTan = tanService.generateVerificationTeleTan(); + String teleTan = tanService.generateVerificationTeleTan(TeleTanType.TEST); VerificationTan teleTanFromDB = tanService.getEntityByTan(teleTan).orElse(null); if (teleTanFromDB != null) { teleTanFromDB.setRedeemed(true); @@ -215,7 +218,7 @@ public void verifyUnknownTeleTan() { @Test public void verifyExpiredTeleTan() { - String teleTan = tanService.generateVerificationTeleTan(); + String teleTan = tanService.generateVerificationTeleTan(TeleTanType.TEST); VerificationTan teleTanFromDB = tanService.getEntityByTan(teleTan).orElse(null); LocalDateTime validFrom = LocalDateTime.now().minusHours(1).minusMinutes(1); if (teleTanFromDB != null) { @@ -226,6 +229,19 @@ public void verifyExpiredTeleTan() { assertFalse(tanService.verifyTeleTan(teleTan)); } + @Test + public void verifyExpiredEventTeleTan() { + String teleTan = tanService.generateVerificationTeleTan(TeleTanType.EVENT); + VerificationTan teleTanFromDB = tanService.getEntityByTan(teleTan).orElse(null); + LocalDateTime validFrom = LocalDateTime.now().minusDays(2).minusMinutes(1); + if (teleTanFromDB != null) { + teleTanFromDB.setValidFrom(validFrom); + teleTanFromDB.setValidUntil(validFrom.plusDays(2)); + } + tanService.saveTan(teleTanFromDB); + assertFalse(tanService.verifyTeleTan(teleTan)); + } + @Test public void testTeleTANFormat() { assertThat(tanService.isTeleTanValid("29ABCZAE4E")).isTrue(); @@ -248,11 +264,11 @@ public void testRateLimitCheckForTeleTan() { assertThat(tanService.isTeleTanRateLimitNotExceeded()).isTrue(); - for (int i = 0; i < TELE_TAN_RATE_LIMIT_COUNT - 1; i++) tanService.generateVerificationTeleTan(); + for (int i = 0; i < TELE_TAN_RATE_LIMIT_COUNT - 1; i++) tanService.generateVerificationTeleTan(TeleTanType.TEST); assertThat(tanService.isTeleTanRateLimitNotExceeded()).isTrue(); - tanService.generateVerificationTeleTan(); + tanService.generateVerificationTeleTan(TeleTanType.TEST); assertThat(tanService.isTeleTanRateLimitNotExceeded()).isFalse(); } @@ -264,7 +280,7 @@ public void testRateLimitShouldNotCountNonTeleTan() { assertThat(tanService.isTeleTanRateLimitNotExceeded()).isTrue(); - for (int i = 0; i < TELE_TAN_RATE_LIMIT_COUNT + 1; i++) tanService.generateVerificationTan(TEST_TAN_SOURCE_OF_TRUST); + for (int i = 0; i < TELE_TAN_RATE_LIMIT_COUNT + 1; i++) tanService.generateVerificationTan(TEST_TAN_SOURCE_OF_TRUST, null); assertThat(tanService.isTeleTanRateLimitNotExceeded()).isTrue(); } @@ -276,18 +292,18 @@ public void testRateLimitShouldNotCountTeleTansOlderThanDefinedTimeWindow() { assertThat(tanService.isTeleTanRateLimitNotExceeded()).isTrue(); - VerificationTan tan1 = tanService.generateVerificationTan("tan1", TanType.TELETAN, TEST_TELE_TAN_SOURCE_OF_TRUST); - VerificationTan tan2 = tanService.generateVerificationTan("tan2", TanType.TELETAN, TEST_TELE_TAN_SOURCE_OF_TRUST); + VerificationTan tan1 = tanService.generateVerificationTan("tan1", TanType.TELETAN, TEST_TELE_TAN_SOURCE_OF_TRUST, TeleTanType.TEST); + VerificationTan tan2 = tanService.generateVerificationTan("tan2", TanType.TELETAN, TEST_TELE_TAN_SOURCE_OF_TRUST, TeleTanType.TEST); tan1.setCreatedAt(LocalDateTime.now().minusSeconds(TELE_TAN_RATE_LIMIT_SECONDS + 1)); tan2.setCreatedAt(LocalDateTime.now().minusSeconds(TELE_TAN_RATE_LIMIT_SECONDS + 1)); tanService.saveTan(tan1); tanService.saveTan(tan2); - for (int i = 0; i < TELE_TAN_RATE_LIMIT_COUNT - 1; i++) tanService.generateVerificationTeleTan(); + for (int i = 0; i < TELE_TAN_RATE_LIMIT_COUNT - 1; i++) tanService.generateVerificationTeleTan(TeleTanType.TEST); assertThat(tanService.isTeleTanRateLimitNotExceeded()).isTrue(); - tanService.generateVerificationTeleTan(); + tanService.generateVerificationTeleTan(TeleTanType.TEST); assertThat(tanService.isTeleTanRateLimitNotExceeded()).isFalse(); } @@ -295,7 +311,7 @@ public void testRateLimitShouldNotCountTeleTansOlderThanDefinedTimeWindow() { /** * Check Tele-TAN syntax constraints. * - * @param tan the Tele TAN + * @param teleTan the Tele TAN * @return Tele TAN verification flag */ private boolean syntaxTanVerification(String teleTan) {