5
0
mirror of https://github.com/AJMicke/KickerELO.git synced 2026-03-11 13:31:02 +01:00

Merge branch 'sso' into feature/sign-in

This commit is contained in:
Moritz
2025-06-13 14:34:26 +02:00
committed by GitHub
17 changed files with 119 additions and 44 deletions

7
.gitignore vendored
View File

@@ -2,6 +2,13 @@ target/
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/ !**/src/main/**/target/
!**/src/test/**/target/ !**/src/test/**/target/
data.mv.db
src/main/bundles
node_modules
# for secrets
.env
.vscode
### IntelliJ IDEA ### ### IntelliJ IDEA ###
.idea/modules.xml .idea/modules.xml

View File

@@ -1,7 +1,7 @@
# KickerELO # KickerELO
KickerELO is a web application for displaying Elo ratings for foosball (table soccer) games. KickerELO is a web application for displaying Elo ratings for foosball (table soccer) games.
It uses **Spring Boot** for the backend, **Vaadin** for the frontend, and **MariaDB** as the database. It uses **Spring Boot** for the backend, **Vaadin** for the frontend, and **MariaDB** as the database. It is compatible with any OpenID Connect (OIDC) provider.
## Requirements ## Requirements

View File

@@ -38,6 +38,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId> <artifactId>vaadin-spring-boot-starter</artifactId>
@@ -71,6 +75,10 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>

View File

@@ -1,13 +1,14 @@
package org.kickerelo.kickerelo; package org.kickerelo.kickerelo;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.server.PWA;
import com.vaadin.flow.theme.Theme;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.server.PWA;
import com.vaadin.flow.theme.Theme;
@SpringBootApplication @SpringBootApplication
@EntityScan(basePackages = "org.kickerelo.kickerelo.data") @EntityScan(basePackages = "org.kickerelo.kickerelo.data")
@@ -19,5 +20,4 @@ public class KickerEloApplication implements AppShellConfigurator {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(KickerEloApplication.class, args); SpringApplication.run(KickerEloApplication.class, args);
} }
} }

View File

@@ -0,0 +1,25 @@
package org.kickerelo.kickerelo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import com.vaadin.flow.spring.security.VaadinWebSecurity;
@Configuration
class SecurityConfiguration extends VaadinWebSecurity {
@Override
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/app/admin/**", "/app/admin", "/app/app/admin/**", "/app/app/admin").hasAuthority("Kicker Admin")
.anyRequest().permitAll()
)
.oauth2Login(org.springframework.security.config.Customizer.withDefaults())
.logout(logout -> logout.logoutSuccessUrl("/"))
.csrf(csrf -> csrf.disable());
}
}

View File

@@ -15,6 +15,11 @@ import com.vaadin.flow.router.Layout;
import org.kickerelo.kickerelo.util.AccessControlService; import org.kickerelo.kickerelo.util.AccessControlService;
import org.kickerelo.kickerelo.views.*; import org.kickerelo.kickerelo.views.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
@Layout @Layout
@JsModule("./prefers-color-scheme.js") @JsModule("./prefers-color-scheme.js")
public class KickerAppLayout extends AppLayout { public class KickerAppLayout extends AppLayout {
@@ -32,6 +37,7 @@ public class KickerAppLayout extends AppLayout {
// Add login/logout button // Add login/logout button
if (accessControlService.userAllowedForRole("")) { if (accessControlService.userAllowedForRole("")) {
Anchor logoutLink = new Anchor("/logout", "Logout"); Anchor logoutLink = new Anchor("/logout", "Logout");
logoutLink.getElement().getStyle() logoutLink.getElement().getStyle()
.set("margin-left", "auto") .set("margin-left", "auto")
.set("margin-right", "10px") .set("margin-right", "10px")

View File

@@ -0,0 +1,12 @@
package org.kickerelo.kickerelo.util;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class RedirectController {
@GetMapping("/")
public String redirectToApp() {
return "redirect:/app";
}
}

View File

@@ -12,6 +12,7 @@ import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant; import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
@Route("admin") @Route("admin")

View File

@@ -1,5 +1,12 @@
package org.kickerelo.kickerelo.views; package org.kickerelo.kickerelo.views;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.service.KickerEloService;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.H2;
@@ -8,14 +15,8 @@ import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.IntegerField; import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.service.KickerEloService;
@Route(value = "enter1vs1") @Route("enter1vs1")
public class Enter1vs1View extends VerticalLayout { public class Enter1vs1View extends VerticalLayout {
public Enter1vs1View(KickerEloService eloService) { public Enter1vs1View(KickerEloService eloService) {

View File

@@ -1,5 +1,12 @@
package org.kickerelo.kickerelo.views; package org.kickerelo.kickerelo.views;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.service.KickerEloService;
import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.H2;
@@ -8,14 +15,8 @@ import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.IntegerField; import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.service.KickerEloService;
@Route(value = "enter2vs2") @Route("enter2vs2")
public class Enter2vs2View extends VerticalLayout { public class Enter2vs2View extends VerticalLayout {
public Enter2vs2View(KickerEloService eloService) { public Enter2vs2View(KickerEloService eloService) {
H2 subheading = new H2("2 vs 2 Ergebnis"); H2 subheading = new H2("2 vs 2 Ergebnis");

View File

@@ -3,13 +3,13 @@ package org.kickerelo.kickerelo.views;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.kickerelo.kickerelo.repository.SpielerRepository;
import org.kickerelo.kickerelo.util.comparator.Spieler1vs1EloComparator;
import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.repository.SpielerRepository;
import org.kickerelo.kickerelo.util.comparator.Spieler1vs1EloComparator;
@Route("graph1vs1") @Route("graph1vs1")
public class Graph1vs1View extends VerticalLayout { public class Graph1vs1View extends VerticalLayout {

View File

@@ -3,13 +3,13 @@ package org.kickerelo.kickerelo.views;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.kickerelo.kickerelo.repository.SpielerRepository;
import org.kickerelo.kickerelo.util.comparator.Spieler2vs2EloComparator;
import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.repository.SpielerRepository;
import org.kickerelo.kickerelo.util.comparator.Spieler2vs2EloComparator;
@Route("graph2vs2") @Route("graph2vs2")
public class Graph2vs2View extends VerticalLayout { public class Graph2vs2View extends VerticalLayout {

View File

@@ -1,5 +1,10 @@
package org.kickerelo.kickerelo.views; package org.kickerelo.kickerelo.views;
import java.util.List;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
import org.kickerelo.kickerelo.repository.Ergebnis1vs1Repository;
import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridSortOrder; import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.grid.dataview.GridListDataView; import com.vaadin.flow.component.grid.dataview.GridListDataView;
@@ -13,10 +18,6 @@ import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer; import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
import com.vaadin.flow.data.value.ValueChangeMode; import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
import org.kickerelo.kickerelo.repository.Ergebnis1vs1Repository;
import java.util.List;
@Route("history1vs1") @Route("history1vs1")
public class History1vs1View extends HistoryView { public class History1vs1View extends HistoryView {

View File

@@ -1,5 +1,10 @@
package org.kickerelo.kickerelo.views; package org.kickerelo.kickerelo.views;
import java.util.List;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridSortOrder; import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.grid.dataview.GridListDataView; import com.vaadin.flow.component.grid.dataview.GridListDataView;
@@ -14,10 +19,6 @@ import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer; import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
import com.vaadin.flow.data.value.ValueChangeMode; import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import java.util.List;
@Route("history2vs2") @Route("history2vs2")

View File

@@ -1,17 +1,18 @@
package org.kickerelo.kickerelo.views; package org.kickerelo.kickerelo.views;
import java.util.List;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.service.KickerEloService;
import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridSortOrder; import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.data.provider.SortDirection; import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.service.KickerEloService;
import java.util.List; @Route("/")
@Route("")
public class PlayerListView extends VerticalLayout { public class PlayerListView extends VerticalLayout {
public PlayerListView(KickerEloService eloService) { public PlayerListView(KickerEloService eloService) {
setSizeFull(); setSizeFull();

View File

@@ -1,5 +1,11 @@
package org.kickerelo.kickerelo.views; package org.kickerelo.kickerelo.views;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import org.kickerelo.kickerelo.service.KickerEloService;
import org.kickerelo.kickerelo.service.Stat2vs2Service;
import org.kickerelo.kickerelo.util.Position;
import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.NativeLabel; import com.vaadin.flow.component.html.NativeLabel;
@@ -8,11 +14,6 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.progressbar.ProgressBar; import com.vaadin.flow.component.progressbar.ProgressBar;
import com.vaadin.flow.component.progressbar.ProgressBarVariant; import com.vaadin.flow.component.progressbar.ProgressBarVariant;
import com.vaadin.flow.router.Route; import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import org.kickerelo.kickerelo.service.KickerEloService;
import org.kickerelo.kickerelo.service.Stat2vs2Service;
import org.kickerelo.kickerelo.util.Position;
@Route("stat2vs2") @Route("stat2vs2")
public class Stat2vs2View extends VerticalLayout { public class Stat2vs2View extends VerticalLayout {

View File

@@ -10,5 +10,15 @@ spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false spring.jpa.show-sql=false
spring.jpa.open-in-view=false spring.jpa.open-in-view=false
# == OIDC Configuration ==
spring.security.oauth2.client.registration.oidc.client-id=${OIDC_CLIENT_ID}
spring.security.oauth2.client.registration.oidc.client-secret=${OIDC_CLIENT_SECRET}
spring.security.oauth2.client.registration.oidc.scope=openid,email,profile
spring.security.oauth2.client.registration.oidc.redirect-uri=${OIDC_REDIRECT_URI}
spring.security.oauth2.client.provider.oidc.jwk-set-uri=${OIDC_JWK_SET_URI}
spring.security.oauth2.client.provider.oidc.issuer-uri=${OIDC_ISSUER_URI}
vaadin.urlMapping=/app/*
# In prod mode, never add the test data to the database # In prod mode, never add the test data to the database
spring.sql.init.mode=never spring.sql.init.mode=never