chore: Initial import of FLEX training material

This commit is contained in:
Alexander Kobjolke 2024-11-07 21:02:53 +01:00
parent c01246d4f7
commit 12235acc42
1020 changed files with 53940 additions and 0 deletions

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.accso</groupId>
<artifactId>flexinale-distributed</artifactId>
<version>2024.3.0</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>flexinale-distributed-security</artifactId>
<version>2024.3.0</version>
<name>Flexinale Distributed Security</name>
<description>Flexinale - FLEX case-study &quot;film festival&quot;, distributed services, security</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${org-postgres.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${apache-poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${apache-poi.version}</version>
</dependency>
<dependency>
<groupId>de.accso</groupId>
<artifactId>flexinale-distributed-common</artifactId>
<version>2024.3.0</version>
</dependency>
<dependency>
<groupId>de.accso</groupId>
<artifactId>flexinale-distributed-security_api_contract</artifactId>
<version>2024.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>${spotbugs-maven-plugin.version}</version>
<configuration>
<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>${findsecbugs-maven-plugin.version}</version>
</plugin>
</plugins>
</configuration>
<dependencies>
<!-- overwrite dependency on spotbugs if you want to specify the version of spotbugs -->
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs</artifactId>
<version>${spotbugs.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,61 @@
package de.accso.flexinale.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@Profile({ "!testdata-ticketing" })
@EnableWebSecurity
public class ApplicationSecurityConfiguration {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
@Bean
public AuthenticationManager authManager(final HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(authenticationProvider());
return authenticationManagerBuilder.build();
}
@Bean
public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
// see here: https://www.baeldung.com/spring-security-csrf
// otherwise, the rest POST requests fail with 401 errors
http.csrf(c -> c.ignoringRequestMatchers("/rest/**"));
return http.build();
}
}

View file

@ -0,0 +1,50 @@
package de.accso.flexinale.security;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import de.accso.flexinale.security.api_contract.BesucherRetriever;
import de.accso.flexinale.security.infrastructure.persistence.BenutzerEntity;
import de.accso.flexinale.security.infrastructure.persistence.BenutzerJpaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@Service
@Profile({ "!testdata-ticketing" })
public class BenutzerDetailsService implements UserDetailsService, BesucherRetriever {
@Autowired
private BenutzerJpaRepository benutzerJpaRepository;
@Override
public UserDetails loadUserByUsername(final String login) throws UsernameNotFoundException {
Optional<BenutzerEntity> benutzer = benutzerJpaRepository.findByLogin(login);
if (benutzer.isEmpty()) { // login is unique
throw new UsernameNotFoundException("no Benutzer %s found".formatted(login));
}
return new UserPrincipal(benutzer.get());
}
@Override
public Identifiable.Id getIdOfLoggedInBesucher() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String userLogin = authentication.getName();
Optional<BenutzerEntity> benutzer = benutzerJpaRepository.findByLogin(userLogin);
if (benutzer.isEmpty()) {
throw new ResponseStatusException(HttpStatus.EXPECTATION_FAILED,
"None or more than one Besucher with login " + userLogin);
}
else {
return benutzer.get().id();
}
}
}

View file

@ -0,0 +1,8 @@
package de.accso.flexinale.security;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}

View file

@ -0,0 +1,43 @@
package de.accso.flexinale.security;
import de.accso.flexinale.security.infrastructure.persistence.BenutzerEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
public class UserPrincipal implements UserDetails {
private final BenutzerEntity benutzer;
public UserPrincipal(BenutzerEntity user) {
this.benutzer = user;
}
@Override
@SuppressWarnings("Convert2Lambda")
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
GrantedAuthority authority = new GrantedAuthority() {
@Override
public String getAuthority() {
return benutzer.rolle.toString();
}
};
authorities.add(authority);
return authorities;
}
@Override
public String getPassword() {
return benutzer.getPasswort();
}
@Override
public String getUsername() {
return benutzer.login;
}
}

View file

@ -0,0 +1,31 @@
package de.accso.flexinale.security.infrastructure;
import de.accso.flexinale.security.infrastructure.persistence.BenutzerDao;
import de.accso.flexinale.security.infrastructure.persistence.BenutzerUploadService;
import de.accso.flexinale.security.infrastructure.persistence.BenutzerJpaRepository;
import de.accso.flexinale.security.infrastructure.persistence.BenutzerJpaRepositoryDelegate;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@Profile({ "!testdata-ticketing" })
@EnableJpaRepositories({"de.accso.flexinale.security.infrastructure.persistence"})
@EnableTransactionManagement
@EntityScan(basePackages={"de.accso.flexinale.security.infrastructure.persistence"})
public class FlexinaleSecuritySpringFactory {
// ------------------------------------------------------------------------------------------------
@Bean
public BenutzerUploadService createBenutzerUploadService(final BenutzerDao benutzerDao) {
return new BenutzerUploadService(benutzerDao);
}
@Bean
public BenutzerDao createBenutzerDao(final BenutzerJpaRepository benutzerJpaRepository) {
return new BenutzerJpaRepositoryDelegate(benutzerJpaRepository);
}
}

View file

@ -0,0 +1,17 @@
package de.accso.flexinale.security.infrastructure.persistence;
import de.accso.flexinale.common.domain.model.AbstractDao;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import java.util.List;
import java.util.Optional;
public interface BenutzerDao extends AbstractDao<BenutzerEntity> {
@Override
Optional<BenutzerEntity> findById(final Identifiable.Id id);
List<BenutzerEntity> findByName(final String name);
Optional<BenutzerEntity> findByLogin(final String login);
}

View file

@ -0,0 +1,133 @@
package de.accso.flexinale.security.infrastructure.persistence;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import de.accso.flexinale.common.shared_kernel.Mergeable;
import de.accso.flexinale.common.shared_kernel.Versionable;
import jakarta.persistence.EnumType;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.io.Serializable;
@jakarta.persistence.Entity(name="Benutzer")
@SuppressWarnings("unused")
public class BenutzerEntity implements Identifiable, Versionable, Mergeable<BenutzerEntity>, Serializable {
@SuppressWarnings("unused")
public enum Rolle { // Spring Security needs "ROLE_" as default prefix! Do not change.
ROLE_UNBEKANNT,
ROLE_BESUCHER,
ROLE_ADMIN
}
@jakarta.persistence.Id
public String id; // primary key
@jakarta.persistence.Column(unique = true)
public String login;
@jakarta.persistence.Column(unique = true)
public String emailAdresse;
@jakarta.persistence.Column
public String name;
@jakarta.persistence.Column
public String vorname;
@jakarta.persistence.Column
@jakarta.persistence.Enumerated(EnumType.STRING)
public Rolle rolle;
@jakarta.persistence.Column
private String passwort;
@jakarta.persistence.Version
private Integer version = 0;
protected BenutzerEntity() {
}
public BenutzerEntity(final String id,
final String login, final String emailAdresse, final String name, final String vorname) {
this(id, Versionable.unknownVersion().version(), login, emailAdresse, name, vorname, Rolle.ROLE_UNBEKANNT);
}
public BenutzerEntity(final String id, final Integer version,
final String login, final String emailAdresse, final String name, final String vorname) {
this(id, version, login, emailAdresse, name, vorname, Rolle.ROLE_UNBEKANNT);
}
public BenutzerEntity(final String id,
final String login, final String emailAdresse, final String name, final String vorname, Rolle rolle) {
this(id, Versionable.unknownVersion().version(), login, emailAdresse, name, vorname, rolle);
}
public BenutzerEntity(final String id, final Integer version,
final String login, final String emailAdresse, final String name, final String vorname, Rolle rolle) {
this.id = id;
this.version = version;
this.login = login;
this.emailAdresse = emailAdresse;
this.name = name;
this.vorname = vorname;
this.rolle = rolle;
}
@Override
public Id id() {
return Identifiable.Id.of(id);
}
@Override
public BenutzerEntity merge(final BenutzerEntity newData) {
return new BenutzerEntity(this.id, this.version,
newData.login, newData.emailAdresse, newData.name, newData.vorname, newData.rolle);
}
@Override
public Version version() {
return Versionable.Version.of(version);
}
public String getPasswort() {
return passwort;
}
public void encryptAndSetPassword(final String passwort) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
this.passwort = encoder.encode(passwort);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
@Override
public boolean equals(final Object o) {
if (this == o) {return true;}
if (o == null || getClass() != o.getClass()) {return false;}
BenutzerEntity that = (BenutzerEntity) o;
return new EqualsBuilder()
.append(id, that.id).append(version, that.version)
.append(login, that.login)
.append(emailAdresse, that.emailAdresse)
.append(name, that.name)
.append(vorname, that.vorname).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(id).append(version)
.append(login)
.append(emailAdresse).append(name).append(vorname).toHashCode();
}
}

View file

@ -0,0 +1,21 @@
package de.accso.flexinale.security.infrastructure.persistence;
import org.springframework.data.annotation.Immutable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Immutable
@Repository
public interface BenutzerJpaRepository extends JpaRepository<BenutzerEntity, String> {
@Query("SELECT b FROM Benutzer b WHERE b.name = :name")
List<BenutzerEntity> findByName(@Param("name") String name);
@Query("SELECT b FROM Benutzer b WHERE b.login = :login")
Optional<BenutzerEntity> findByLogin(@Param("login") String login);
}

View file

@ -0,0 +1,56 @@
package de.accso.flexinale.security.infrastructure.persistence;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import java.util.List;
import java.util.Optional;
@SuppressWarnings("unused")
public class BenutzerJpaRepositoryDelegate implements BenutzerDao {
private final BenutzerJpaRepository benutzerJpaRepository;
public BenutzerJpaRepositoryDelegate(BenutzerJpaRepository BenutzerJpaRepository) {
this.benutzerJpaRepository = BenutzerJpaRepository;
}
@Override
public List<BenutzerEntity> findAll() {
return benutzerJpaRepository.findAll();
}
@Override
public Optional<BenutzerEntity> findById(final Identifiable.Id id) {
return benutzerJpaRepository.findById(id.id());
}
@Override
public List<BenutzerEntity> findByName(final String name) {
return benutzerJpaRepository.findByName(name);
}
@Override
public Optional<BenutzerEntity> findByLogin(final String login) {
return benutzerJpaRepository.findByLogin(login);
}
@Override
public BenutzerEntity save(final BenutzerEntity benutzerEntity) {
return benutzerJpaRepository.save(benutzerEntity);
}
@Override
public void delete(final BenutzerEntity benutzerEntity) {
benutzerJpaRepository.delete(benutzerEntity);
}
@Override
public void deleteById(final Identifiable.Id id) {
benutzerJpaRepository.deleteById(id.id());
}
@Override
public void deleteAll() {
benutzerJpaRepository.deleteAll();
}
}

View file

@ -0,0 +1,60 @@
package de.accso.flexinale.security.infrastructure.persistence;
import de.accso.flexinale.common.domain.model.AbstractDao;
import de.accso.flexinale.common.application.services.AbstractExcelDataUploadService;
import de.accso.flexinale.common.shared_kernel.Versionable;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class BenutzerUploadService extends AbstractExcelDataUploadService<BenutzerEntity> {
private static final Logger LOGGER = LoggerFactory.getLogger(BenutzerUploadService.class);
private final BenutzerDao benutzerEntityDao;
public BenutzerUploadService(final BenutzerDao benutzerEntityDao) {
this.benutzerEntityDao = benutzerEntityDao;
}
@Override
public AbstractDao<BenutzerEntity> getDao() {
return benutzerEntityDao;
}
@Override
public String getNameOfExcelDataType() {
return "Benutzer";
}
@Override
public BenutzerEntity createDataFromExcelRow(final Row excelRow) {
String benutzerId = excelRow.getCell(0).getStringCellValue();
// set authorization roles
Cell cellRolle = excelRow.getCell(6);
BenutzerEntity.Rolle rolle;
if (cellRolle == null || cellRolle.getCellType() == CellType.BLANK) {
rolle = null;
}
else {
rolle = BenutzerEntity.Rolle.valueOf(cellRolle.getStringCellValue());
}
BenutzerEntity benutzer = new BenutzerEntity(
benutzerId, Versionable.unknownVersion().version(),
excelRow.getCell(1).getStringCellValue(),
excelRow.getCell(3).getStringCellValue(),
excelRow.getCell(4).getStringCellValue(),
excelRow.getCell(5).getStringCellValue(),
rolle
);
benutzer.encryptAndSetPassword(excelRow.getCell(2).getStringCellValue());
LOGGER.debug("New Benutzer created: " + benutzer);
return benutzer;
}
}

View file

@ -0,0 +1,20 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYYMMdd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.orm.jpa" level="INFO"/>
<logger name="org.springframework.boot.autoconfigure.domain.EntityScan" level="INFO"/>
<logger name="org.apache.kafka" level="WARN"/>
<logger name="org.apache.kafka.clients.admin.AdminClient" level="INFO"/>
<logger name="org.apache.kafka.clients.consumer.ConsumerConfig" level="INFO"/>
<logger name="org.apache.kafka.clients.producer.ProducerConfig" level="INFO"/>
<logger name="de.accso" level="INFO"/>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>