Skip to content

Commit

Permalink
Merge branch 'release/2023.11.15'
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabien committed Nov 15, 2023
2 parents 3756f45 + ddf133e commit fb2d150
Show file tree
Hide file tree
Showing 91 changed files with 762 additions and 3,470 deletions.
Original file line number Diff line number Diff line change
@@ -1,40 +1,50 @@
package fr.dossierfacile.api.dossierfacileapiowner.property;

import fr.dossierfacile.api.dossierfacileapiowner.mail.MailService;
import fr.dossierfacile.common.entity.ApartmentSharing;
import fr.dossierfacile.common.entity.Property;
import fr.dossierfacile.common.entity.PropertyApartmentSharing;
import fr.dossierfacile.common.entity.Tenant;
import fr.dossierfacile.common.enums.TenantType;
import fr.dossierfacile.common.repository.PropertyLogRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import static fr.dossierfacile.common.entity.PropertyLog.*;
import java.util.Optional;

@Slf4j
@Service
@AllArgsConstructor
public class PropertyApartmentSharingServiceImpl implements PropertyApartmentSharingService {
private final PropertyApartmentSharingRepository propertyApartmentSharingRepository;
private final PropertyLogRepository logRepository;
private final MailService mailService;

@Override
public void deletePropertyApartmentSharing(PropertyApartmentSharing propertyApartmentSharing) {
propertyApartmentSharingRepository.delete(propertyApartmentSharing);
logRepository.save(applicationDeletedByOwner(propertyApartmentSharing));
}

@Override
public void subscribeTenantApartmentSharingToProperty(Tenant tenant, Property property, boolean hasAccess) {
if (tenant.getTenantType() == TenantType.CREATE) {
PropertyApartmentSharing propertyApartmentSharing = propertyApartmentSharingRepository
.findByPropertyAndApartmentSharing(property, tenant.getApartmentSharing())
.orElse(PropertyApartmentSharing.builder()
.accessFull(hasAccess)
.token(hasAccess ? tenant.getApartmentSharing().getToken() : tenant.getApartmentSharing().getTokenPublic())
.property(property)
.apartmentSharing(tenant.getApartmentSharing())
.build()
);
propertyApartmentSharingRepository.save(propertyApartmentSharing);
mailService.sendEmailNewApplicant(tenant, property.getOwner(), property);
ApartmentSharing apartmentSharing = tenant.getApartmentSharing();
Optional<PropertyApartmentSharing> existingPropertyApartmentSharing = propertyApartmentSharingRepository
.findByPropertyAndApartmentSharing(property, apartmentSharing);
if (existingPropertyApartmentSharing.isEmpty()) {
PropertyApartmentSharing propertyApartmentSharing = PropertyApartmentSharing.builder()
.accessFull(hasAccess)
.token(hasAccess ? apartmentSharing.getToken() : apartmentSharing.getTokenPublic())
.property(property)
.apartmentSharing(apartmentSharing)
.build();
propertyApartmentSharingRepository.save(propertyApartmentSharing);
logRepository.save(applicationReceived(property, apartmentSharing));
mailService.sendEmailNewApplicant(tenant, property.getOwner(), property);
}
} else {
throw new IllegalStateException("Tenant is not the main tenant");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.dossierfacile.api.dossierfacileapiowner.register;

import fr.dossierfacile.common.converter.AcquisitionData;
import fr.dossierfacile.common.entity.Owner;

public interface AuthenticationFacade {
Expand All @@ -8,5 +9,6 @@ public interface AuthenticationFacade {

Owner getOwner();

String getKeycloakClientId();
Owner getOwner(AcquisitionData acquisitionData);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.base.Strings;
import fr.dossierfacile.api.dossierfacileapiowner.log.OwnerLogService;
import fr.dossierfacile.api.dossierfacileapiowner.user.OwnerRepository;
import fr.dossierfacile.common.converter.AcquisitionData;
import fr.dossierfacile.common.entity.Owner;
import fr.dossierfacile.common.enums.OwnerLogType;
import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -70,14 +71,19 @@ private String getFranceConnectBirthDate() {

@Override
public Owner getOwner() {
return getOwner(null);
}

@Override
public Owner getOwner(AcquisitionData acquisitionData) {
if (!keycloakService.isKeycloakUser(getKeycloakUserId())) {
throw new AccessDeniedException("invalid token");
}
Optional<Owner> optionalOwner = ownerRepository.findByKeycloakId(getKeycloakUserId());
if (optionalOwner.isEmpty()) {
optionalOwner = ownerRepository.findByEmail(getUserEmail());
Optional<Owner> existingOwner = ownerRepository.findByKeycloakId(getKeycloakUserId());
if (existingOwner.isEmpty()) {
existingOwner = ownerRepository.findByEmail(getUserEmail());
}
Owner owner = optionalOwner.orElse(Owner.builder().email(getUserEmail()).build());
Owner owner = existingOwner.orElse(Owner.builder().email(getUserEmail()).build());
owner.setKeycloakId(getKeycloakUserId());
owner.setFranceConnect(isFranceConnect());
if (isFranceConnect()) {
Expand All @@ -89,16 +95,16 @@ public Owner getOwner() {
owner.setLastName(getLastName());
owner.setPreferredName(getPreferredName());
}
if (existingOwner.isEmpty() && acquisitionData != null) {
owner.setAcquisitionCampaign(acquisitionData.campaign());
owner.setAcquisitionSource(acquisitionData.source());
owner.setAcquisitionMedium(acquisitionData.medium());
}
owner = ownerRepository.saveAndFlush(owner);
if (optionalOwner.isEmpty()) {
if (existingOwner.isEmpty()) {
ownerLogService.saveLog(OwnerLogType.ACCOUNT_CREATED_VIA_KC, owner.getId());
}
return owner;
}

@Override
public String getKeycloakClientId() {
return ((Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getClaimAsString("azp");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@

import fr.dossierfacile.api.dossierfacileapiowner.register.AuthenticationFacade;
import fr.dossierfacile.api.dossierfacileapiowner.register.KeycloakService;
import fr.dossierfacile.common.converter.AcquisitionData;
import fr.dossierfacile.common.entity.Owner;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpResponseException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;

import javax.servlet.http.HttpServletResponse;

import java.time.LocalDateTime;

import static org.springframework.http.ResponseEntity.ok;

@RestController
Expand Down Expand Up @@ -55,8 +55,8 @@ public ResponseEntity<Void> logout() {
}

@GetMapping(value = "/profile")
public ResponseEntity<OwnerModel> profile() {
Owner owner = authenticationFacade.getOwner();
public ResponseEntity<OwnerModel> profile(@RequestParam MultiValueMap<String, String> params) {
Owner owner = authenticationFacade.getOwner(AcquisitionData.from(params));
ownerService.updateLastLoginDate(owner);
return ok(ownerMapper.toOwnerModel(owner));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.dossierfacile.api.front.controller;

import fr.dossierfacile.common.converter.AcquisitionData;
import fr.dossierfacile.api.front.form.ShareFileByMailForm;
import fr.dossierfacile.api.front.mapper.PropertyOMapper;
import fr.dossierfacile.api.front.mapper.TenantMapper;
Expand All @@ -17,12 +18,14 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import static org.springframework.http.ResponseEntity.badRequest;
Expand All @@ -43,8 +46,8 @@ public class TenantController {
private final UserService userService;

@GetMapping(value = "/profile", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<TenantModel> profile() {
Tenant tenant = authenticationFacade.getLoggedTenant();
public ResponseEntity<TenantModel> profile(@RequestParam MultiValueMap<String, String> params) {
Tenant tenant = authenticationFacade.getLoggedTenant(AcquisitionData.from(params));
tenantService.updateLastLoginDateAndResetWarnings(tenant);
return ok(tenantMapper.toTenantModel(tenant));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public ResponseEntity<ConnectedTenantModel> profilePartner() {

if (tenant == null) {
KeycloakUser kcUser = authenticationFacade.getKeycloakUser();
tenant = tenantService.registerFromKeycloakUser(kcUser, partner);
tenant = tenantService.registerFromKeycloakUser(kcUser, partner, null);
} else {
userService.linkTenantToPartner(tenant, partner, null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package fr.dossierfacile.api.front.security;

import fr.dossierfacile.api.front.exception.TenantNotFoundException;
import fr.dossierfacile.common.converter.AcquisitionData;
import fr.dossierfacile.api.front.model.KeycloakUser;
import fr.dossierfacile.api.front.security.interfaces.AuthenticationFacade;
import fr.dossierfacile.api.front.service.interfaces.DocumentService;
import fr.dossierfacile.api.front.service.interfaces.TenantPermissionsService;
import fr.dossierfacile.api.front.service.interfaces.TenantService;
import fr.dossierfacile.api.front.service.interfaces.TenantStatusService;
import fr.dossierfacile.api.front.util.SentryUtil;
import fr.dossierfacile.common.entity.Tenant;
import fr.dossierfacile.common.enums.DocumentCategory;
import fr.dossierfacile.common.enums.LogType;
import fr.dossierfacile.common.enums.TenantFileStatus;
import fr.dossierfacile.common.repository.TenantCommonRepository;
import fr.dossierfacile.common.service.interfaces.LogService;
import io.sentry.SentryLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -106,19 +105,35 @@ public Tenant getTenant(Long tenantId) {

@Override
public Tenant getLoggedTenant() {
return getLoggedTenant(null);
}

@Override
public Tenant getLoggedTenant(AcquisitionData acquisitionData) {
KeycloakUser kcUser = getKeycloakUser();
if (!kcUser.isEmailVerified() && !kcUser.isFranceConnect()) {
throw new AccessDeniedException("Email is not verified" + kcUser.getEmail());
}
Tenant tenant = tenantRepository.findByKeycloakId(kcUser.getKeycloakId());
if (tenant == null) {
log.error(SentryUtil.captureMessage("User try to connect with not found keycloakId " + kcUser.getKeycloakId(), SentryLevel.ERROR));
tenant = tenantRepository.findByEmail(kcUser.getEmail())
.orElseGet(() -> tenantService.registerFromKeycloakUser(kcUser, null));
}
Tenant tenant = findOrCreateTenant(kcUser, acquisitionData);
return synchronizeTenant(tenant, kcUser);
}

private Tenant findOrCreateTenant(KeycloakUser kcUser, AcquisitionData acquisitionData) {
String keycloakId = kcUser.getKeycloakId();
Tenant tenant = tenantRepository.findByKeycloakId(keycloakId);
if (tenant != null) {
return tenant;
}
log.warn("No tenant account found associated with keycloakId {}", keycloakId);
Optional<Tenant> tenantByEmail = tenantRepository.findByEmail(kcUser.getEmail());
if (tenantByEmail.isPresent()) {
log.info("Found tenant by email from keycloak");
return tenantByEmail.get();
}
log.info("Creating tenant account associated with keycloakId {}", keycloakId);
return tenantService.registerFromKeycloakUser(kcUser, null, acquisitionData);
}

private Tenant synchronizeTenant(Tenant tenant, KeycloakUser user) {
// check if some data should be updated
if (!matches(tenant, user)) {
Expand All @@ -133,6 +148,9 @@ private Tenant synchronizeTenant(Tenant tenant, KeycloakUser user) {
if (!Boolean.TRUE.equals(tenant.getFranceConnect()) && user.isFranceConnect()) {
log.info("Local account link to FranceConnect account, for tenant with ID {}", tenant.getId());
logService.saveLog(LogType.FC_ACCOUNT_LINK, tenant.getId());
} else if (tenant.getKeycloakId() == null ){
log.info("First tenant connection from DF, for tenant with ID {}", tenant.getId());
logService.saveLog(LogType.ACCOUNT_LINK, tenant.getId());
}
tenant.setKeycloakId(user.getKeycloakId());

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.dossierfacile.api.front.security.interfaces;

import fr.dossierfacile.common.converter.AcquisitionData;
import fr.dossierfacile.api.front.model.KeycloakUser;
import fr.dossierfacile.common.entity.Tenant;

Expand All @@ -14,9 +15,9 @@ public interface AuthenticationFacade {

Tenant getLoggedTenant();

Tenant getLoggedTenant(AcquisitionData acquisitionData);

Tenant getTenant(Long id);

String getFranceConnectLink(String redirectUri);


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import fr.dossierfacile.api.front.exception.MailSentLimitException;
import fr.dossierfacile.api.front.exception.TenantNotFoundException;
import fr.dossierfacile.common.converter.AcquisitionData;
import fr.dossierfacile.api.front.model.KeycloakUser;
import fr.dossierfacile.api.front.model.tenant.EmailExistsModel;
import fr.dossierfacile.api.front.model.tenant.TenantModel;
Expand All @@ -10,7 +11,6 @@
import fr.dossierfacile.api.front.register.form.partner.EmailExistsForm;
import fr.dossierfacile.api.front.service.interfaces.KeycloakService;
import fr.dossierfacile.api.front.service.interfaces.MailService;
import fr.dossierfacile.api.front.service.interfaces.PropertyService;
import fr.dossierfacile.api.front.service.interfaces.TenantService;
import fr.dossierfacile.api.front.service.interfaces.UserApiService;
import fr.dossierfacile.api.front.util.Obfuscator;
Expand Down Expand Up @@ -52,7 +52,6 @@ public class TenantServiceImpl implements TenantService {
private final LogService logService;
private final MailService mailService;
private final PartnerCallBackService partnerCallBackService;
private final PropertyService propertyService;
private final RegisterFactory registerFactory;
private final TenantCommonRepository tenantRepository;
private final KeycloakService keycloakService;
Expand Down Expand Up @@ -109,7 +108,7 @@ public Tenant findByKeycloakId(String keycloakId) {

@Override
@Transactional
public Tenant registerFromKeycloakUser(KeycloakUser kcUser, String partner) {
public Tenant registerFromKeycloakUser(KeycloakUser kcUser, String partner, AcquisitionData acquisitionData) {
// check user still exists in keycloak
if (keycloakService.getKeyCloakUser(kcUser.getKeycloakId()) == null) {
throw new TenantNotFoundException("User doesn't exist anymore in KC - token is out-of-date");
Expand All @@ -125,6 +124,12 @@ public Tenant registerFromKeycloakUser(KeycloakUser kcUser, String partner) {
.honorDeclaration(false)
.build());

if (acquisitionData != null) {
tenant.setAcquisitionCampaign(acquisitionData.campaign());
tenant.setAcquisitionSource(acquisitionData.source());
tenant.setAcquisitionMedium(acquisitionData.medium());
}

if (!kcUser.isEmailVerified()) {
// createdAccount without verified email should be deactivated
keycloakService.disableAccount(kcUser.getKeycloakId());
Expand All @@ -140,7 +145,7 @@ public Tenant registerFromKeycloakUser(KeycloakUser kcUser, String partner) {
if (kcUser.isFranceConnect()) {
logService.saveLog(LogType.FC_ACCOUNT_CREATION, tenant.getId());
} else {
logService.saveLog(LogType.ACCOUNT_CREATED, tenant.getId());
logService.saveLog(LogType.ACCOUNT_CREATED_VIA_KC, tenant.getId());
}
return tenantRepository.save(tenant);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.dossierfacile.api.front.service.interfaces;

import fr.dossierfacile.common.converter.AcquisitionData;
import fr.dossierfacile.api.front.model.KeycloakUser;
import fr.dossierfacile.api.front.model.tenant.EmailExistsModel;
import fr.dossierfacile.api.front.model.tenant.TenantModel;
Expand All @@ -26,7 +27,7 @@ public interface TenantService {

Tenant findByKeycloakId(String keycloakId);

Tenant registerFromKeycloakUser(KeycloakUser kcUser, String partner);
Tenant registerFromKeycloakUser(KeycloakUser kcUser, String partner, AcquisitionData acquisitionData);

Optional<Tenant> findByEmail(String email);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.hasRole("ADMIN")
.antMatchers("/bo/tenant/{tenantId}/processFile")
.hasAnyRole("ADMIN", "OPERATOR", "PARTNER")
.antMatchers("/bo/**", "/bo", "/documents/**")
.antMatchers("/bo/**", "/bo", "/documents/**", "/bo/dashboard")
.hasAnyRole("ADMIN", "OPERATOR")
.anyRequest()
.authenticated()
Expand Down
Loading

0 comments on commit fb2d150

Please sign in to comment.