de.frachtwerk.essencium.backend.configuration.initialization.DefaultRightInitializer Maven / Gradle / Ivy
/*
* Copyright (C) 2024 Frachtwerk GmbH, Leopoldstraße 7C, 76133 Karlsruhe.
*
* This file is part of essencium-backend.
*
* essencium-backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* essencium-backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with essencium-backend. If not, see .
*/
package de.frachtwerk.essencium.backend.configuration.initialization;
import de.frachtwerk.essencium.backend.model.Right;
import de.frachtwerk.essencium.backend.repository.RoleRepository;
import de.frachtwerk.essencium.backend.security.BasicApplicationRight;
import de.frachtwerk.essencium.backend.service.RightService;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@Configuration
@RequiredArgsConstructor
public class DefaultRightInitializer implements DataInitializer {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRightInitializer.class);
private final RightService rightService;
private final RoleRepository roleRepository;
@Override
public int order() {
return 20;
}
/**
* Providing basic rights used by the essencium-backend library.
*
* NOTE: These are non-persistent rights. Persistent rights can be fetched with the provided
* names of this rights by using the RightService
*
* @return a set of non-persistent basic application rights
*/
public Set getBasicApplicationRights() {
return Stream.of(BasicApplicationRight.values())
.map(r -> new Right(r.name(), r.getDescription()))
.collect(Collectors.toSet());
}
/**
* Providing additional rights used by the implementing application
*
* This function can be overwritten by the implementing application to provide rights the
* database shall be initialized with on start up if they don't exist.
*
*
NOTE: the rights must not be persistent!
*
* @return set of additional application rights that shall be initialized
*/
public Set getAdditionalApplicationRights() {
return new HashSet<>();
}
protected final Stream getCombinedRights(Stream methods, Right... entities) {
return methods.flatMap(
method ->
Stream.of(entities)
.map(
entity ->
new Right(entity.getAuthority() + "_" + method, entity.getDescription())));
}
protected final Stream getCombinedRights(Stream methods, String... entities) {
return methods
.flatMap(method -> Stream.of(entities).map(entity -> entity + "_" + method))
.map(s -> new Right(s, ""));
}
@Override
@Transactional
public void run() {
Map existingRights =
rightService.getAll().stream()
.collect(Collectors.toMap(Right::getAuthority, Function.identity()));
final Set basicRights = getBasicApplicationRights();
final Set additionalRights = getAdditionalApplicationRights();
final Set allRights =
Stream.concat(basicRights.stream(), additionalRights.stream()).collect(Collectors.toSet());
final var basicAuthorities =
basicRights.stream().map(Right::getAuthority).collect(Collectors.toSet());
final var additionalAuthorities =
additionalRights.stream().map(Right::getAuthority).collect(Collectors.toSet());
final var intersectingAuthority =
CollectionUtils.findFirstMatch(basicAuthorities, additionalAuthorities);
if (intersectingAuthority != null) {
throw new IllegalStateException(
"Additional right has same authority as basic right[" + intersectingAuthority + "]");
}
createMissingRights(allRights, existingRights);
updateExistingRights(allRights, existingRights);
deleteObsoleteRights(existingRights, allRights);
}
private void createMissingRights(Set allRights, Map existingRights) {
allRights.stream()
.filter(r -> !existingRights.containsKey(r.getAuthority()))
.forEach(
right -> {
LOGGER.info("Initializing right [{}]", right.getAuthority());
rightService.save(right);
});
}
private void updateExistingRights(Set allRights, Map existingRights) {
allRights.stream()
.filter(right -> existingRights.containsKey(right.getAuthority()))
.filter(
r ->
!Objects.equals(
r.getDescription(), existingRights.get(r.getAuthority()).getDescription()))
.forEach(
right -> {
LOGGER.info("Updating right [{}]", right.getAuthority());
rightService.save(right);
});
}
private void deleteObsoleteRights(Map existingRights, Set allRights) {
existingRights.values().stream()
.filter(r -> !allRights.contains(r))
.forEach(
r -> {
LOGGER.info("Deleting right [{}]", r.getAuthority());
roleRepository
.findAllByRights_Authority(r.getAuthority())
.forEach(
role -> {
role.getRights().remove(r);
roleRepository.save(role);
});
rightService.deleteByAuthority(r.getAuthority());
});
}
}