securing with nextcloud oidc
This commit is contained in:
@ -1,14 +1,14 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.1.3'
|
id 'org.springframework.boot' version '3.2.0'
|
||||||
id "io.spring.dependency-management" version "1.1.2"
|
id "io.spring.dependency-management" version "1.1.4"
|
||||||
id 'war'
|
id 'war'
|
||||||
id 'eclipse'
|
id 'eclipse'
|
||||||
id 'application'
|
id 'application'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'de.jottyfan.bico'
|
group = 'de.jottyfan.bico'
|
||||||
version = '0.0.5'
|
version = '0.0.6'
|
||||||
|
|
||||||
description = """BibleClassOrganizer"""
|
description = """BibleClassOrganizer"""
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ dependencies {
|
|||||||
implementation 'de.jottyfan:bicolib:4'
|
implementation 'de.jottyfan:bicolib:4'
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-jooq'
|
implementation 'org.springframework.boot:spring-boot-starter-jooq'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
@ -4,7 +4,6 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -12,8 +11,8 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableWebSecurity
|
|
||||||
public class Main extends SpringBootServletInitializer {
|
public class Main extends SpringBootServletInitializer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||||
return application.sources(Main.class);
|
return application.sources(Main.class);
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
package de.jottyfan.bico.config;
|
package de.jottyfan.bico.config;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
||||||
import org.springframework.security.core.userdetails.User;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -17,18 +11,15 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(UserDetailsService.class)
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
InMemoryUserDetailsManager imudm() {
|
http.authorizeHttpRequests(
|
||||||
return new InMemoryUserDetailsManager(User.withUsername("user").password("{noop}password").roles("USER").build());
|
// @formatter:off
|
||||||
}
|
r -> r.requestMatchers("/", "/error", "/css/**", "/js/**", "/webjars/**", "/template").permitAll()
|
||||||
|
.requestMatchers("/**").authenticated())
|
||||||
@Bean
|
.oauth2Login(l -> l.authorizationEndpoint(e -> e.baseUri("/oauth2/authorize-client")));
|
||||||
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
|
// @formatter:on
|
||||||
DefaultAuthenticationEventPublisher daep(ApplicationEventPublisher delegate) {
|
return http.build();
|
||||||
return new DefaultAuthenticationEventPublisher(delegate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
package de.jottyfan.bico.modules;
|
package de.jottyfan.bico.modules;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
|
||||||
import de.jottyfan.bico.modules.profile.ProfileService;
|
import de.jottyfan.bico.modules.profile.ProfileService;
|
||||||
|
|
||||||
@ -15,15 +22,31 @@ public abstract class CommonController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ProfileService profileService;
|
private ProfileService profileService;
|
||||||
|
|
||||||
|
@Value("${spring.security.oauth2.client.provider.nextcloud.issuer-uri}")
|
||||||
|
private String nextcloudUrl;
|
||||||
|
|
||||||
|
@ModelAttribute("hasBUrole")
|
||||||
|
public Boolean hasBURole(Principal principal) {
|
||||||
|
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) principal;
|
||||||
|
OAuth2User user = token.getPrincipal();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<String> roles = (List<String>) user.getAttributes().get("roles");
|
||||||
|
return roles.contains("Bibelunterricht");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the theme for the current session
|
* get the theme for the current session
|
||||||
*
|
*
|
||||||
* @return the theme; light or dark at the moment
|
* @return the theme; light or dark at the moment
|
||||||
*/
|
*/
|
||||||
public Model useThemedModel(Model model) {
|
@ModelAttribute("theme")
|
||||||
// TODO: add profile's user name
|
public String getTheme() {
|
||||||
String username = "jotty";
|
String username = SecurityContextHolder.getContext().getAuthentication().getName();
|
||||||
model.addAttribute("theme", profileService.getTheme(username));
|
return profileService.getTheme(username);
|
||||||
return model;
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("nextcloudUrl")
|
||||||
|
public String getNextcloudUrl() {
|
||||||
|
return nextcloudUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package de.jottyfan.bico.modules.index;
|
package de.jottyfan.bico.modules.index;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
import de.jottyfan.bico.modules.CommonController;
|
import de.jottyfan.bico.modules.CommonController;
|
||||||
@ -14,8 +13,7 @@ import de.jottyfan.bico.modules.CommonController;
|
|||||||
@Controller
|
@Controller
|
||||||
public class IndexController extends CommonController {
|
public class IndexController extends CommonController {
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String getIndex(Model model) {
|
public String getIndex() {
|
||||||
useThemedModel(model);
|
|
||||||
return "redirect:/sheet";
|
return "redirect:/sheet";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ public class SheetController extends CommonController {
|
|||||||
|
|
||||||
@GetMapping("/sheet")
|
@GetMapping("/sheet")
|
||||||
public String getSheet(Model model) {
|
public String getSheet(Model model) {
|
||||||
useThemedModel(model).addAttribute("list", service.getList());
|
model.addAttribute("list", service.getList());
|
||||||
return "/sheet";
|
return "/sheet";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nextcloud.url",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "the URL to nextcloud for logout operations"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -10,4 +10,11 @@ spring.datasource.password = ${db.password}
|
|||||||
server.servlet.context-path = ${my.context-path:/BiCO}
|
server.servlet.context-path = ${my.context-path:/BiCO}
|
||||||
|
|
||||||
# for development only
|
# for development only
|
||||||
server.port = 8081
|
server.port = ${server.port}
|
||||||
|
|
||||||
|
# nextcloud open ID connection
|
||||||
|
spring.security.oauth2.client.provider.nextcloud.issuer-uri = ${nextcloud.issuer-uri}
|
||||||
|
spring.security.oauth2.client.registration.nextcloud.client-id = ${nextcloud.client-id}
|
||||||
|
spring.security.oauth2.client.registration.nextcloud.client-secret = ${nextcloud.client-secret}
|
||||||
|
spring.security.oauth2.client.registration.nextcloud.authorization-grant-type = authorization_code
|
||||||
|
spring.security.oauth2.client.registration.nextcloud.redirect-uri = ${nextcloud.redirect-uri}
|
||||||
|
10
src/main/resources/templates/error.html
Normal file
10
src/main/resources/templates/error.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
|
||||||
|
<body>
|
||||||
|
<th:block layout:fragment="content">
|
||||||
|
<div class="borderdist">
|
||||||
|
<div class="alert alert-danger">Es ist ein Fehler aufgetreten. Wenden Sie sich bitte an Ihren Entwickler.</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -3,7 +3,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<th:block layout:fragment="content">
|
<th:block layout:fragment="content">
|
||||||
<div class="borderdist">
|
<div class="borderdist">
|
||||||
<div class="container">
|
<div class="container" sec:authorize="hasRole('Bibelunterricht')">
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h2>Dozent-Reservierung</h2>
|
<h2>Dozent-Reservierung</h2>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<th:block layout:fragment="content">
|
<th:block layout:fragment="content">
|
||||||
<div class="borderdist">
|
<div class="borderdist">
|
||||||
<table id="table" class="table table-striped">
|
<table id="table" class="table table-striped" sec:authorize="hasRole('Bibelunterricht')">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Tag</th>
|
<th>Tag</th>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
|
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
|
||||||
<body>
|
<body>
|
||||||
<th:block layout:fragment="content">
|
<th:block layout:fragment="content">
|
||||||
<div class="borderdist">
|
<div class="borderdist" sec:authorize="hasRole('Bibelunterricht')">
|
||||||
<div class="alert alert-danger" th:if="${bean}">
|
<div class="alert alert-danger" th:if="${bean}">
|
||||||
Wollen Sie den Slot <span th:text="${#temporals.format(bean.slotDay, 'dd.MM.yyyy')}"></span> wirklich löschen?<br />
|
Wollen Sie den Slot <span th:text="${#temporals.format(bean.slotDay, 'dd.MM.yyyy')}"></span> wirklich löschen?<br />
|
||||||
<a th:href="@{/slot/{id}/destroy(id=${bean.pkSlot})}" class="btn btn-outline-danger" th:if="${bean.pkSlot}">Ja, definitiv</a>
|
<a th:href="@{/slot/{id}/destroy(id=${bean.pkSlot})}" class="btn btn-outline-danger" th:if="${bean.pkSlot}">Ja, definitiv</a>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<th:block layout:fragment="content">
|
<th:block layout:fragment="content">
|
||||||
<div class="borderdist">
|
<div class="borderdist">
|
||||||
<div class="container">
|
<div class="container" sec:authorize="hasRole('Bibelunterricht')">
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h2>Terminfestlegung</h2>
|
<h2>Terminfestlegung</h2>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent" style="margin-right: 20px">
|
<div class="collapse navbar-collapse" id="navbarSupportedContent" style="margin-right: 20px">
|
||||||
<ul class="navbar-nav mb-2 mb-lg-0">
|
<ul class="navbar-nav mb-2 mb-lg-0" th:if="${hasBUrole}">
|
||||||
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/}" style="margin-left: 12px">Einteilung</a></li>
|
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/}" style="margin-left: 12px">Einteilung</a></li>
|
||||||
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/subject/list}" style="margin-left: 12px">Themen</a></li>
|
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/subject/list}" style="margin-left: 12px">Themen</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -34,11 +34,16 @@
|
|||||||
<a href="#" class="btn btn-outline-secondary" onclick="toggleDarkMode()"><i class="bi bi-moon"></i></a>
|
<a href="#" class="btn btn-outline-secondary" onclick="toggleDarkMode()"><i class="bi bi-moon"></i></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a th:href="@{/logout}" class="btn btn-outline-secondary">abmelden</a>
|
<a th:href="@{${nextcloudUrl}}" class="btn btn-outline-secondary">→ nextcloud</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div layout:fragment="content">content</div>
|
<div layout:fragment="content" th:if="${hasBUrole}">content</div>
|
||||||
|
<div th:unless="${hasBUrole}">
|
||||||
|
<div class="borderdist">
|
||||||
|
<div class="alert alert-danger">Leider fehlen Ihnen die Berechtigungen, um diese Anwendung nutzen zu können.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -3,7 +3,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<th:block layout:fragment="content">
|
<th:block layout:fragment="content">
|
||||||
<div class="borderdist">
|
<div class="borderdist">
|
||||||
<div class="container">
|
<div class="container" sec:authorize="hasRole('Bibelunterricht')">
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<h1>
|
<h1>
|
||||||
Themen für den <span th:text="${#temporals.format(day, 'dd.MM.yyyy')}"></span>
|
Themen für den <span th:text="${#temporals.format(day, 'dd.MM.yyyy')}"></span>
|
||||||
|
Reference in New Issue
Block a user