Skip to content

Commit

Permalink
Merge branch 'VIC-2105-legaltext-in-multiple-languages' of https://gi…
Browse files Browse the repository at this point in the history
…thub.com/virtualidentityag/vi-saas-tenantService into VIC-2126-Accept_changed_T_Cs_and_privacy_policy

� Conflicts:
�	src/main/java/com/vi/tenantservice/api/facade/TenantServiceFacade.java
  • Loading branch information
adnanalicic committed Dec 7, 2022
2 parents ccadcb4 + 22479fb commit 3f0ed94
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.vi.tenantservice.api.exception.TenantAuthorisationException;
import com.vi.tenantservice.api.exception.TenantValidationException;
import javax.ws.rs.BadRequestException;
import com.vi.tenantservice.api.exception.httpresponse.HttpStatusExceptionReason;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
Expand All @@ -11,13 +11,19 @@
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.ws.rs.BadRequestException;

@ControllerAdvice
public class ExceptionHandlerAdvice extends ResponseEntityExceptionHandler {

@ExceptionHandler(value = {TenantValidationException.class})
protected ResponseEntity<Object> handle(RuntimeException ex, WebRequest request) {

var customHttpHeader = ((TenantValidationException) ex).getCustomHttpHeaders();
HttpStatusExceptionReason statusExceptionReason = ((TenantValidationException) ex).getStatusExceptionReason();
if (HttpStatusExceptionReason.LANGUAGE_KEY_NOT_VALID.equals(statusExceptionReason)) {
return handleExceptionInternal(ex, "", customHttpHeader, HttpStatus.BAD_REQUEST, request);
}
return handleExceptionInternal(ex, "", customHttpHeader, HttpStatus.CONFLICT, request);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import com.vi.tenantservice.api.facade.TenantServiceFacade;
import com.vi.tenantservice.api.model.BasicTenantLicensingDTO;
import com.vi.tenantservice.api.model.MultilingualTenantDTO;
import com.vi.tenantservice.api.model.RestrictedTenantDTO;
import com.vi.tenantservice.api.model.TenantDTO;
import com.vi.tenantservice.api.model.MultilingualTenantDTO;
import com.vi.tenantservice.config.security.AuthorisationService;
import com.vi.tenantservice.generated.api.controller.TenantApi;
import com.vi.tenantservice.generated.api.controller.TenantadminApi;
Expand Down Expand Up @@ -71,6 +71,7 @@ public ResponseEntity<MultilingualTenantDTO> createTenant(@Valid MultilingualTen
@Override
@PreAuthorize("hasAnyAuthority('tenant-admin', 'single-tenant-admin')")
public ResponseEntity<MultilingualTenantDTO> updateTenant(Long id, @Valid MultilingualTenantDTO tenantDTO) {

log.info("Updating tenant with id {} by user {} ", id, authorisationService.getUsername());
var updatedTenantDTO = tenantServiceFacade.updateTenant(id, tenantDTO);
return new ResponseEntity<>(updatedTenantDTO, HttpStatus.OK);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ public class TenantValidationException extends RuntimeException {
private final HttpHeaders customHttpHeaders;
private final HttpStatus httpStatus;

private final HttpStatusExceptionReason statusExceptionReason;

public TenantValidationException(HttpStatusExceptionReason exceptionReason) {
super();
this.customHttpHeaders = new CustomHttpHeader(exceptionReason)
.buildHeader();
this.httpStatus = HttpStatus.CONFLICT;
this.statusExceptionReason = exceptionReason;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ public enum HttpStatusExceptionReason {
SUBDOMAIN_NOT_UNIQUE,
NOT_ALLOWED_TO_CHANGE_SUBDOMAIN,
NOT_ALLOWED_TO_CHANGE_LICENSING,
NOT_ALLOWED_TO_CHANGE_SETTING
NOT_ALLOWED_TO_CHANGE_SETTING,

LANGUAGE_KEY_NOT_VALID;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.vi.tenantservice.api.facade;


import com.google.common.collect.Lists;
import com.vi.tenantservice.api.converter.TenantConverter;
import com.vi.tenantservice.api.exception.TenantNotFoundException;
import com.vi.tenantservice.api.exception.TenantValidationException;
import com.vi.tenantservice.api.exception.httpresponse.HttpStatusExceptionReason;
import com.vi.tenantservice.api.model.BasicTenantLicensingDTO;
import com.vi.tenantservice.api.model.MultilingualTenantDTO;
import com.vi.tenantservice.api.model.MultilingualContent;
import com.vi.tenantservice.api.model.RestrictedTenantDTO;
import com.vi.tenantservice.api.model.TenantDTO;
import com.vi.tenantservice.api.model.TenantEntity;
import com.vi.tenantservice.api.model.MultilingualTenantDTO;
import com.vi.tenantservice.api.service.TenantService;
import com.vi.tenantservice.api.service.TranslationService;
import com.vi.tenantservice.api.validation.TenantInputSanitizer;
Expand All @@ -21,7 +24,10 @@
import org.springframework.stereotype.Service;

import javax.ws.rs.BadRequestException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

Expand All @@ -47,18 +53,47 @@ public class TenantServiceFacade {
public MultilingualTenantDTO createTenant(MultilingualTenantDTO tenantDTO) {
log.info("Creating new tenant");
MultilingualTenantDTO sanitizedTenantDTO = tenantInputSanitizer.sanitize(tenantDTO);
validateTenantInput(tenantDTO);
var entity = tenantConverter.toEntity(sanitizedTenantDTO);
setContentActivationDates(entity, tenantDTO);
return tenantConverter.toMultilingualDTO(tenantService.create(entity));
}

public MultilingualTenantDTO updateTenant(Long id, MultilingualTenantDTO tenantDTO) {
tenantFacadeAuthorisationService.assertUserIsAuthorizedToAccessTenant(id);
validateTenantInput(tenantDTO);
MultilingualTenantDTO sanitizedTenantDTO = tenantInputSanitizer.sanitize(tenantDTO);
log.info("Attempting to update tenant with id {}", id);
return updateWithSanitizedInput(id, sanitizedTenantDTO);
}

private void validateTenantInput(MultilingualTenantDTO tenantDTO) {
var isoCountries = Arrays.stream(Locale.getISOCountries()).collect(Collectors.toList());
validateContent(tenantDTO, isoCountries);
}

private void validateContent(MultilingualTenantDTO tenantDTO, List<String> isoCountries) {
if (tenantDTO.getContent() != null) {
validateTranslationKeys(isoCountries, getLanguageUppercaseKeys(tenantDTO.getContent().getImpressum()));
validateTranslationKeys(isoCountries, getLanguageUppercaseKeys(tenantDTO.getContent().getPrivacy()));
validateTranslationKeys(isoCountries, getLanguageUppercaseKeys(tenantDTO.getContent().getTermsAndConditions()));
validateTranslationKeys(isoCountries, getLanguageUppercaseKeys(tenantDTO.getContent().getClaim()));
}
}

private void validateTranslationKeys(List<String> isoCountries, List<String> keys) {
if (!keys.isEmpty() && !isoCountries.containsAll(keys)) {
throw new TenantValidationException(HttpStatusExceptionReason.LANGUAGE_KEY_NOT_VALID);
}
}

private static List<String> getLanguageUppercaseKeys(Map<String, String> translatedMap) {
if (translatedMap == null) {
return Lists.newArrayList();
}
return translatedMap.keySet().stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
}

private MultilingualTenantDTO updateWithSanitizedInput(Long id, MultilingualTenantDTO sanitizedTenantDTO) {
var tenantById = tenantService.findTenantById(id);
if (tenantById.isPresent()) {
Expand Down
25 changes: 14 additions & 11 deletions src/main/java/com/vi/tenantservice/api/service/TenantService.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package com.vi.tenantservice.api.service;

import static com.vi.tenantservice.api.exception.httpresponse.HttpStatusExceptionReason.SUBDOMAIN_NOT_UNIQUE;

import com.vi.tenantservice.api.exception.TenantValidationException;
import com.vi.tenantservice.api.model.TenantEntity;
import com.vi.tenantservice.api.repository.TenantRepository;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;

import static com.vi.tenantservice.api.exception.httpresponse.HttpStatusExceptionReason.SUBDOMAIN_NOT_UNIQUE;

@Service
@Slf4j
@RequiredArgsConstructor
Expand All @@ -27,9 +28,7 @@ public class TenantService {


public TenantEntity create(TenantEntity tenantEntity) {
if (!multitenancyWithSingleDomain) {
validateTenantSubdomainDoesNotExist(tenantEntity);
}
validateTenant(tenantEntity);
setCreateAndUpdateDate(tenantEntity);
return tenantRepository.save(tenantEntity);
}
Expand All @@ -51,11 +50,15 @@ private boolean tenantWithSuchSubdomainAlreadyExists(TenantEntity tenantEntity,
}

public TenantEntity update(TenantEntity tenantEntity) {
validateTenant(tenantEntity);
tenantEntity.setUpdateDate(LocalDateTime.now(ZoneOffset.UTC));
return tenantRepository.save(tenantEntity);
}

private void validateTenant(TenantEntity tenantEntity) {
if (!multitenancyWithSingleDomain) {
validateTenantSubdomainDoesNotExist(tenantEntity);
}
tenantEntity.setUpdateDate(LocalDateTime.now(ZoneOffset.UTC));
return tenantRepository.save(tenantEntity);
}

public Optional<TenantEntity> findTenantById(Long id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,34 @@ void updateTenant_Should_sanitizeInput_When_calledWithExistingTenantIdAndForTena
.andExpect(jsonPath("$.content.claim['de']").value("<b>claim</b>"));
}

@Test
void updateTenant_Should_throwValidationException_When_calledWithExistingTenantIdAndForTenantAdminAuthorityButInvalidLanguageCode()
throws Exception {
String jsonRequest = prepareRequestWithInvalidLanguageContent();

AuthenticationMockBuilder builder = new AuthenticationMockBuilder();
mockMvc.perform(put(EXISTING_TENANT_VIA_ADMIN)
.with(authentication(builder.withAuthority(TENANT_ADMIN.getValue()).build()))
.contentType(APPLICATION_JSON)
.content(jsonRequest)
.contentType(APPLICATION_JSON))
.andExpect(status().isBadRequest());
}

private String prepareRequestWithInvalidScriptContent() {
return multilingualTenantTestDataBuilder.withId(1L).withName(appendMalciousScript("name"))
.withSubdomain(appendMalciousScript("subdomain"))
.withContent(appendMalciousScript("<b>impressum</b>"), appendMalciousScript("<b>claim</b>"))
.jsonify();
}

private String prepareRequestWithInvalidLanguageContent() {
return multilingualTenantTestDataBuilder.withId(1L).withName(appendMalciousScript("name"))
.withSubdomain(appendMalciousScript("subdomain"))
.withTranslatedImpressum("abc", "impressum")
.jsonify();
}

private String appendMalciousScript(String content) {
return content + SCRIPT_CONTENT;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,14 @@ public MultilingualTenantTestDataBuilder withContent(String impressum, String cl
tenantMultilingualDTO.setContent(content);
return this;
}

public MultilingualTenantTestDataBuilder withTranslatedImpressum(String language, String impressum) {
MultilingualContent content = new MultilingualContent();
var translatedMap = new HashMap<String, String>();
translatedMap.put(language, impressum);
content.setImpressum(translatedMap);
content.setClaim(defaultTranslations(""));
tenantMultilingualDTO.setContent(content);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.HashMap;
import java.util.Map;

Expand Down

0 comments on commit 3f0ed94

Please sign in to comment.