Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@
*/
@ControllerAdvice
public class RestResponseEntityExceptionHandler {

@ExceptionHandler(value = { UserAdminException.class })
protected ResponseEntity<Object> handleException(RuntimeException exception) {
if (exception instanceof UserAdminException) {
UserAdminException userAdminException = (UserAdminException) exception;
if (userAdminException.getType().equals(FORBIDDEN)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(userAdminException.getType());
}
if (exception instanceof UserAdminException userAdminException) {
return switch (userAdminException.getType()) {
case FORBIDDEN -> ResponseEntity.status(HttpStatus.FORBIDDEN).body(userAdminException.getType());
};
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,12 @@ public ResponseEntity<Void> userExists(@PathVariable("sub") String sub) {
public ResponseEntity<List<ConnectionEntity>> getConnections(@RequestHeader("userId") String userId) {
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(service.getConnections(userId));
}

@Deprecated(forRemoval = true)
@GetMapping(value = "/connections/deduplicate")
@Operation(summary = "get the connections deduplicated", deprecated = true)
@ApiResponse(responseCode = "200", description = "The connections list")
public ResponseEntity<List<ConnectionEntity>> deduplicateConnections(@RequestHeader("userId") String userId) {
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(service.getDeduplicatedConnections(userId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
*/
package org.gridsuite.useradmin.server;

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.Objects;

/**
* @author Etienne Homer <etienne.homer at rte-france.com>
*/
@EqualsAndHashCode(callSuper = false)
@Data
public class UserAdminException extends RuntimeException {
public enum Type {
FORBIDDEN
Expand All @@ -22,9 +27,4 @@ public UserAdminException(Type type) {
super(Objects.requireNonNull(type.name()));
this.type = type;
}

Type getType() {
return type;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.useradmin.server.repository;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.UUID;

/**
* @author Etienne Homer <etienne.homer at rte-france.com>
*/
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@MappedSuperclass
abstract class AbstractUserEntity {
public AbstractUserEntity(String sub) {
this(UUID.randomUUID(), sub);
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private UUID id;

@Column(name = "sub", nullable = false, unique = true)
private String sub;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
*/
package org.gridsuite.useradmin.server.repository;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.UUID;

/**
* @author Etienne Homer <etienne.homer at rte-france.com>
Expand All @@ -24,25 +26,20 @@
@Setter
@Entity
@Table(name = "connection", indexes = {@Index(name = "connection_sub_index", columnList = "sub")})
public class ConnectionEntity {
public class ConnectionEntity extends AbstractUserEntity {
@Column(name = "firstConnectionDate", nullable = false)
private LocalDateTime firstConnectionDate;

@Id
@Column(name = "id")
private UUID id;

@Column(name = "sub", nullable = false)
private String sub;

@Column(name = "firstConnexionDate", nullable = false)
private LocalDateTime firstConnexionDate;

@Column(name = "lastConnexionDate", nullable = false)
private LocalDateTime lastConnexionDate;
@Column(name = "lastConnectionDate", nullable = false)
private LocalDateTime lastConnectionDate;

@Column(name = "connectionAccepted", nullable = false)
private Boolean connectionAccepted;

public ConnectionEntity(String sub, LocalDateTime firstConnexionDate, LocalDateTime lastConnexionDate, Boolean connectionAccepted) {
this(UUID.randomUUID(), sub, firstConnexionDate, lastConnexionDate, connectionAccepted);
public ConnectionEntity(String sub, LocalDateTime firstConnectionDate, LocalDateTime lastConnectionDate, Boolean connectionAccepted) {
super(sub);
this.firstConnectionDate = firstConnectionDate;
this.lastConnectionDate = lastConnectionDate;
this.connectionAccepted = connectionAccepted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,37 @@
*/
package org.gridsuite.useradmin.server.repository;

import jakarta.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
import java.util.UUID;

/**
* @author Etienne Homer <etienne.homer at rte-france.com>
*/
@Repository
public interface ConnectionRepository extends JpaRepository<ConnectionEntity, UUID> {
List<ConnectionEntity> findBySub(String sub);
@NonNull
Optional<ConnectionEntity> findBySub/*IgnoreCase*/(@NonNull String sub);

@Nullable
ConnectionEntity getBySub(@NonNull String sub);

@Transactional()
@Modifying
default void recordNewConnection(@NonNull final String sub, final boolean connectionAccepted) {
//To avoid consistency issue we truncate the time to microseconds since postgres and h2 can only store with a precision of microseconds
final LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MICROS);
this.findBySub/*IgnoreCase*/(sub).ifPresentOrElse(
conn -> this.save(conn.setLastConnectionDate(now).setConnectionAccepted(connectionAccepted)),
() -> this.save(new ConnectionEntity(sub, now, now, connectionAccepted))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,24 @@
*/
package org.gridsuite.useradmin.server.repository;

import lombok.AllArgsConstructor;
import jakarta.persistence.Entity;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import jakarta.persistence.*;
import java.util.UUID;

/**
* @author Etienne Homer <etienne.homer at rte-france.com>
*/
@NoArgsConstructor
@AllArgsConstructor
//@AllArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "user_infos", indexes = {@Index(name = "user_infos_sub_index", columnList = "sub")})
public class UserInfosEntity {

public class UserInfosEntity extends AbstractUserEntity {
public UserInfosEntity(String sub) {
this(UUID.randomUUID(), sub);
super(sub);
}

@Id
@Column(name = "id")
private UUID id;

@Column(name = "sub", nullable = false)
private String sub;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lombok.accessors.chain = true
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -32,29 +30,22 @@ public ConnectionsService(ConnectionRepository connectionRepository) {

@Transactional
public void recordConnectionAttempt(String sub, Boolean isAllowed) {
ConnectionEntity connectionEntity = connectionRepository.findBySub(sub).stream().findFirst().orElse(null);
if (connectionEntity == null) {
//To avoid consistency issue we truncate the time to microseconds since postgres and h2 can only store a precision of microseconds
connectionEntity = new ConnectionEntity(sub, LocalDateTime.now().truncatedTo(ChronoUnit.MICROS), LocalDateTime.now().truncatedTo(ChronoUnit.MICROS), isAllowed);
connectionRepository.save(connectionEntity);
} else {
connectionEntity.setLastConnexionDate(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS));
connectionEntity.setConnectionAccepted(isAllowed);
}
connectionRepository.recordNewConnection(sub, isAllowed);
}

@Deprecated(forRemoval = true)
@Transactional
public List<ConnectionEntity> removeDuplicates() {
Map<String, List<ConnectionEntity>> connectionsBySub = connectionRepository.findAll().stream().collect(Collectors.groupingBy(ConnectionEntity::getSub));

connectionsBySub.keySet().forEach(sub ->
connectionsBySub.get(sub).stream().skip(1).forEach(connectionEntity -> {
ConnectionEntity groupedEntity = connectionsBySub.get(sub).get(0);
if (connectionEntity.getLastConnexionDate().isAfter(groupedEntity.getLastConnexionDate())) {
groupedEntity.setLastConnexionDate(connectionEntity.getLastConnexionDate());
if (connectionEntity.getLastConnectionDate().isAfter(groupedEntity.getLastConnectionDate())) {
groupedEntity.setLastConnectionDate(connectionEntity.getLastConnectionDate());
}
if (connectionEntity.getFirstConnexionDate().isBefore(groupedEntity.getFirstConnexionDate())) {
groupedEntity.setFirstConnexionDate(connectionEntity.getFirstConnexionDate());
if (connectionEntity.getFirstConnectionDate().isBefore(groupedEntity.getFirstConnectionDate())) {
groupedEntity.setFirstConnectionDate(connectionEntity.getFirstConnectionDate());
}
connectionRepository.delete(connectionEntity);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,34 @@
*/
package org.gridsuite.useradmin.server.service;

import lombok.RequiredArgsConstructor;
import org.gridsuite.useradmin.server.UserAdminApplicationProps;
import org.gridsuite.useradmin.server.UserAdminException;
import org.gridsuite.useradmin.server.repository.ConnectionEntity;
import org.gridsuite.useradmin.server.repository.ConnectionRepository;
import org.gridsuite.useradmin.server.repository.UserAdminRepository;
import org.gridsuite.useradmin.server.repository.UserInfosEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.List;
import java.util.UUID;

import static org.gridsuite.useradmin.server.UserAdminException.Type.FORBIDDEN;

/**
* @author Etienne Homer <etienne.homer at rte-france.com>
*/
@RequiredArgsConstructor
@Service
public class UserAdminService {
private UserAdminRepository userAdminRepository;

private ConnectionsService connectionsService;
private final UserAdminRepository userAdminRepository;
private final ConnectionRepository connectionRepository;
private final ConnectionsService connectionsService;

@Autowired
private UserAdminApplicationProps applicationProps;

public UserAdminService(UserAdminRepository userAdminRepository, ConnectionsService connectionsService) {
this.userAdminRepository = Objects.requireNonNull(userAdminRepository);
this.connectionsService = Objects.requireNonNull(connectionsService);
}

public List<UserInfosEntity> getUsers(String userId) {
if (!isAdmin(userId)) {
throw new UserAdminException(FORBIDDEN);
Expand All @@ -43,6 +42,14 @@ public List<UserInfosEntity> getUsers(String userId) {
}

public List<ConnectionEntity> getConnections(String userId) {
if (!isAdmin(userId)) {
throw new UserAdminException(FORBIDDEN);
}
return connectionRepository.findAll();
}

@Deprecated(forRemoval = true)
public List<ConnectionEntity> getDeduplicatedConnections(String userId) {
if (!isAdmin(userId)) {
throw new UserAdminException(FORBIDDEN);
}
Expand All @@ -65,7 +72,9 @@ public void delete(UUID id, String userId) {
}

public boolean subExists(String sub) {
Boolean isAllowed = applicationProps.getAdmins().isEmpty() && userAdminRepository.count() == 0 || applicationProps.getAdmins().contains(sub) || !userAdminRepository.findAllBySub(sub).isEmpty();
Boolean isAllowed = applicationProps.getAdmins().isEmpty() && userAdminRepository.count() == 0
|| applicationProps.getAdmins().contains(sub)
|| !userAdminRepository.findAllBySub(sub).isEmpty();
connectionsService.recordConnectionAttempt(sub, isAllowed);
return isAllowed.booleanValue();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="chuinetri" id="1704818243021-1">
<comment>Deduplicate rows before adding unique constraints</comment>
<delete tableName="user_infos">
<where>id in (select id from (select row_number() over (partition by sub) as rnb, * from user_infos) subquery where rnb > 1)</where>
</delete>

<sql stripComments="true" dbms="!oracle">
update connection c1 set
first_connexion_date = (select min(c2.first_connexion_date) from connection c2 where c2.sub=c1.sub group by c2.sub),
last_connexion_date = (select max(c2.last_connexion_date) from connection c2 where c2.sub=c1.sub group by c2.sub),
connection_accepted = (select connection_accepted from connection c2 where (c2.sub, c2.last_connexion_date) in (
select c3.sub, max(c3.last_connexion_date) from connection c3 where c3.sub=c1.sub group by c3.sub) limit 1);
</sql>
<sql stripComments="true" dbms="oracle">
--oracle use fetch instead of limit
update connection c1 set
first_connexion_date = (select min(c2.first_connexion_date) from connection c2 where c2.sub=c1.sub group by c2.sub),
last_connexion_date = (select max(c2.last_connexion_date) from connection c2 where c2.sub=c1.sub group by c2.sub),
connection_accepted = (select connection_accepted from connection c2 where (c2.sub, c2.last_connexion_date) in (
select c3.sub, max(c3.last_connexion_date) from connection c3 where c3.sub=c1.sub group by c3.sub) FETCH FIRST 1 ROW ONLY);
</sql>
<delete tableName="connection">
<where>id in (select id from (select row_number() over (partition by sub) as rnb, * from connection) subquery where rnb > 1)</where>
</delete>

<rollback/><!--no rollback possible because delete, but no need as we just removed duplicate records-->
</changeSet>

<changeSet author="chuinetri" id="1704818243021-2">
<addUniqueConstraint columnNames="sub" constraintName="UC_USER_INFOSSUB_COL" tableName="user_infos"/>
<addUniqueConstraint columnNames="sub" constraintName="UC_CONNECTIONSUB_COL" tableName="connection"/>
</changeSet>

<changeSet author="chuinetri" id="1704818243021-3">
<renameColumn tableName="connection" oldColumnName="first_connexion_date" newColumnName="first_connection_date"/>
<renameColumn tableName="connection" oldColumnName="last_connexion_date" newColumnName="last_connection_date"/>
</changeSet>
</databaseChangeLog>
Loading