not yet completed migration to jakarta - still keycloak roles missing

This commit is contained in:
Jörg Henke
2023-07-27 19:35:36 +02:00
parent 059dcadb01
commit f07f8f3c06
33 changed files with 310 additions and 453 deletions

View File

@ -19,7 +19,12 @@
<attribute name="gradle_used_by_scope" value="main,test"/> <attribute name="gradle_used_by_scope" value="main,test"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/> <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer">
<attributes>
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin/default"/> <classpathentry kind="output" path="bin/default"/>
</classpath> </classpath>

View File

@ -10,11 +10,6 @@
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand> <buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name> <name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments> <arguments>
@ -25,6 +20,11 @@
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.jdt.core.javanature</nature>

View File

@ -1,11 +1,14 @@
eclipse.preferences.version=1 eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=11 org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=11 org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=17

View File

@ -1,48 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0"> <?xml version="1.0" encoding="UTF-8"?>
<project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="timetrack"> <wb-module deploy-name="timetrack">
<property name="context-root" value="timetrack"/> <property name="context-root" value="timetrack"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/webapp"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
<wb-resource deploy-path="/" source-path="src/main/webapp"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/java"/>
</wb-module> </wb-module>
</project-modules> </project-modules>

View File

@ -3,5 +3,5 @@
<fixed facet="jst.java"/> <fixed facet="jst.java"/>
<fixed facet="jst.web"/> <fixed facet="jst.web"/>
<installed facet="jst.web" version="2.4"/> <installed facet="jst.web" version="2.4"/>
<installed facet="jst.java" version="11"/> <installed facet="jst.java" version="17"/>
</faceted-project> </faceted-project>

View File

@ -1,19 +1,18 @@
plugins { plugins {
id 'org.springframework.boot' version '2.6.5' id 'org.springframework.boot' version '3.1.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java' id 'java'
id 'war'
} }
group = 'de.jottyfan' apply plugin: 'io.spring.dependency-management'
version = '1.2.8'
sourceCompatibility = '11'
ext['spring-framework.version'] = '5.3.18' group = 'de.jottyfan'
version = '1.2.9'
description = """timetrack""" description = """timetrack"""
sourceCompatibility = 11 sourceCompatibility = 17
targetCompatibility = 11 targetCompatibility = 17
repositories { repositories {
mavenLocal() mavenLocal()
@ -23,12 +22,6 @@ repositories {
// maven { url "https://gitlab.com/jottyfan/libs/-/raw/main" } // maven { url "https://gitlab.com/jottyfan/libs/-/raw/main" }
} }
dependencyManagement {
imports {
mavenBom 'org.keycloak.bom:keycloak-adapter-bom:21.1.1'
}
}
dependencies { dependencies {
implementation 'org.apache.logging.log4j:log4j-api:2.20.0' implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
implementation 'org.apache.logging.log4j:log4j-core:2.20.0' implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
@ -46,14 +39,14 @@ dependencies {
implementation 'org.webjars.bowergithub.datatables:datatables:1.10.21' implementation 'org.webjars.bowergithub.datatables:datatables:1.10.21'
implementation 'org.keycloak:keycloak-spring-boot-starter:17.0.1'
implementation 'org.springframework.boot:spring-boot-starter-jooq' implementation 'org.springframework.boot:spring-boot-starter-jooq'
implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-security'
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
implementation 'org.springframework.security:spring-security-oauth2-authorization-server:1.1.1'
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-test' implementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'de.jottyfan:timetrackjooq:0.1.1' implementation 'de.jottyfan:timetrackjooq:0.1.1'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.0.0' implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.0.0'
@ -64,6 +57,18 @@ dependencies {
testImplementation 'org.springframework.security:spring-security-test' testImplementation 'org.springframework.security:spring-security-test'
} }
war {
doFirst {
manifest {
attributes("Implementation-Title": project.name,
"Implementation-Version": version,
"Implementation-Timestamp": new Date())
}
}
baseName = project.name
version = version
}
test { test {
useJUnitPlatform() useJUnitPlatform()
} }

View File

@ -2,14 +2,21 @@ package de.jottyfan.timetrack;
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.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication @SpringBootApplication
@EnableTransactionManagement @EnableTransactionManagement
public class TimetrackApplication { public class TimetrackApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder application) {
return application.sources(TimetrackApplication.class);
}
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(TimetrackApplication.class, args); SpringApplication.run(TimetrackApplication.class, args);
} }
} }

View File

@ -1,56 +0,0 @@
package de.jottyfan.timetrack.config;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.keycloak.adapters.springsecurity.management.HttpSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@KeycloakConfiguration
@ComponentScan(basePackageClasses = KeycloakSpringBootConfigResolver.class)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
@Override
@ConditionalOnMissingBean(HttpSessionManager.class)
protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/public/**").permitAll();
http.csrf().disable();
}
}

View File

@ -0,0 +1,58 @@
package de.jottyfan.timetrack.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import de.jottyfan.timetrack.config.converter.KeycloakRealmRoleConverter;
/**
*
* @author henkej
*
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfiguration {
@Value("${spring.security.oauth2.client.provider.keycloak.jwk-set-uri}")
private String jwtSetUri;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity sec, InMemoryClientRegistrationRepository crr)
throws Exception {
sec.csrf(o -> o.disable()).anonymous(o -> o.disable())
// @formatter:off
.oauth2Login(o -> o.defaultSuccessUrl("/"))
.logout(o -> o.logoutSuccessHandler(new OidcClientInitiatedLogoutSuccessHandler(crr)))
.authorizeHttpRequests(o -> o.requestMatchers("/public/**").permitAll().anyRequest().authenticated())
.oauth2ResourceServer(o -> o.jwt(j -> j.jwtAuthenticationConverter(getConverter())));
// @formatter:on
return sec.build();
}
private Converter<Jwt, ? extends AbstractAuthenticationToken> getConverter() {
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(new KeycloakRealmRoleConverter());
return jwtConverter;
}
@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(this.jwtSetUri).build();
}
}

View File

@ -0,0 +1,29 @@
package de.jottyfan.timetrack.config.converter;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
/**
*
* @author henkej
*
*/
public class KeycloakRealmRoleConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
@Override
public Collection<GrantedAuthority> convert(Jwt jwt) {
Object o = jwt.getClaims().get("realm_access");
@SuppressWarnings("unchecked")
final Map<String, Object> realmAccess = (Map<String, Object>) o;
Object o2 = realmAccess.get("roles");
@SuppressWarnings("unchecked")
List<String> l = (List<String>) o2;
return l.stream().map(roleName -> "ROLE_" + roleName).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
}

View File

@ -4,13 +4,10 @@ import java.time.Duration;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import javax.annotation.security.RolesAllowed;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -19,8 +16,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import de.jottyfan.timetrack.modules.done.DoneBean; import de.jottyfan.timetrack.modules.done.DoneBean;
import de.jottyfan.timetrack.modules.done.DoneModel; import de.jottyfan.timetrack.modules.done.DoneModel;
import de.jottyfan.timetrack.modules.done.IDoneService; import de.jottyfan.timetrack.modules.done.DoneService;
import de.jottyfan.timetrack.modules.done.SummaryBean; import de.jottyfan.timetrack.modules.done.SummaryBean;
import jakarta.annotation.security.RolesAllowed;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
/** /**
* *
@ -31,15 +31,8 @@ import de.jottyfan.timetrack.modules.done.SummaryBean;
public class IndexController { public class IndexController {
private static final Logger LOGGER = LogManager.getLogger(IndexController.class); private static final Logger LOGGER = LogManager.getLogger(IndexController.class);
private final HttpServletRequest request;
@Autowired @Autowired
private IDoneService doneService; private DoneService doneService;
@Autowired
public IndexController(HttpServletRequest request) {
this.request = request;
}
@GetMapping("/logout") @GetMapping("/logout")
public String getLogout(HttpServletRequest request) throws ServletException { public String getLogout(HttpServletRequest request) throws ServletException {
@ -49,8 +42,8 @@ public class IndexController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@RequestMapping("/") @RequestMapping("/")
public String getIndex(@ModelAttribute DoneModel doneModel, Model model) { public String getIndex(@ModelAttribute DoneModel doneModel, Model model, OAuth2AuthenticationToken token) {
String username = doneService.getCurrentUser(request); String username = doneService.getCurrentUser(token);
Duration maxWorkTime = Duration.ofHours(8); // TODO: to the configuration file Duration maxWorkTime = Duration.ofHours(8); // TODO: to the configuration file
LocalDate day = LocalDate.now(); LocalDate day = LocalDate.now();
List<DoneBean> list = doneService.getList(day, username); List<DoneBean> list = doneService.getList(day, username);

View File

@ -3,13 +3,8 @@ package de.jottyfan.timetrack.modules.contact;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.annotation.security.RolesAllowed;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -20,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import de.jottyfan.timetrack.db.contact.enums.EnumContacttype; import de.jottyfan.timetrack.db.contact.enums.EnumContacttype;
import jakarta.annotation.security.RolesAllowed;
/** /**
* *
@ -28,22 +24,14 @@ import de.jottyfan.timetrack.db.contact.enums.EnumContacttype;
*/ */
@Controller @Controller
public class ContactController { public class ContactController {
private static final Logger LOGGER = LogManager.getLogger(ContactController.class);
private final HttpServletRequest request;
@Autowired @Autowired
private IContactService contactService; private ContactService contactService;
@Autowired
public ContactController(HttpServletRequest request) {
this.request = request;
}
@ModelAttribute("currentUser") @ModelAttribute("currentUser")
@ResponseBody @ResponseBody
public String getCurrentUser() { public String getCurrentUser(OAuth2AuthenticationToken token) {
return contactService.getCurrentUser(request); return contactService.getCurrentUser(token);
} }
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")

View File

@ -31,11 +31,11 @@ import de.jottyfan.timetrack.db.contact.tables.records.TContactRecord;
* *
*/ */
@Repository @Repository
public class ContactGateway { public class ContactRepository {
private static final Logger LOGGER = LogManager.getLogger(ContactGateway.class); private static final Logger LOGGER = LogManager.getLogger(ContactRepository.class);
private final DSLContext jooq; private final DSLContext jooq;
public ContactGateway(@Autowired DSLContext jooq) throws Exception { public ContactRepository(@Autowired DSLContext jooq) throws Exception {
this.jooq = jooq; this.jooq = jooq;
} }

View File

@ -3,13 +3,13 @@ package de.jottyfan.timetrack.modules.contact;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext; import org.jooq.DSLContext;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -20,32 +20,41 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Service @Service
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class ContactService implements IContactService { public class ContactService {
private static final Logger LOGGER = LogManager.getLogger(ContactService.class); private static final Logger LOGGER = LogManager.getLogger(ContactService.class);
@Autowired @Autowired
private DSLContext dsl; private DSLContext dsl;
@Override @Autowired
public String getCurrentUser(HttpServletRequest request) { private OAuth2AuthorizedClientService security;
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
return ksc == null ? "" : ksc.getIdToken().getPreferredUsername(); public String getCurrentUser(OAuth2AuthenticationToken token) {
if (token != null) {
OAuth2AuthorizedClient client = security.loadAuthorizedClient(token.getAuthorizedClientRegistrationId(),
token.getName());
if (client != null) {
return client.getPrincipalName();
} else {
return "client is null";
}
} else {
return "oauth token is null";
}
} }
@Override
public List<ContactBean> getList() { public List<ContactBean> getList() {
try { try {
return new ContactGateway(dsl).getAll(); return new ContactRepository(dsl).getAll();
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e); LOGGER.error(e);
return new ArrayList<>(); return new ArrayList<>();
} }
} }
@Override
public Integer doUpsert(ContactBean bean) { public Integer doUpsert(ContactBean bean) {
try { try {
ContactGateway gw = new ContactGateway(dsl); ContactRepository gw = new ContactRepository(dsl);
return bean.getPk() == null ? gw.add(bean) : gw.update(bean); return bean.getPk() == null ? gw.add(bean) : gw.update(bean);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e); LOGGER.error(e);
@ -53,25 +62,22 @@ public class ContactService implements IContactService {
} }
} }
@Override
public Integer doDelete(Integer pk) { public Integer doDelete(Integer pk) {
try { try {
return new ContactGateway(dsl).delete(pk); return new ContactRepository(dsl).delete(pk);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e); LOGGER.error(e);
return 0; return 0;
} }
} }
@Override
public Integer getAmount() { public Integer getAmount() {
return getList().size(); return getList().size();
} }
@Override
public ContactBean getBean(Integer id) { public ContactBean getBean(Integer id) {
try { try {
return new ContactGateway(dsl).getBean(id); return new ContactRepository(dsl).getBean(id);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e); LOGGER.error(e);
return null; return null;

View File

@ -1,19 +0,0 @@
package de.jottyfan.timetrack.modules.contact;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
/**
*
* @author henkej
*
*/
public interface IContactService {
public List<ContactBean> getList();
public Integer doUpsert(ContactBean bean);
public Integer doDelete(Integer pk);
public Integer getAmount();
public String getCurrentUser(HttpServletRequest request);
public ContactBean getBean(Integer id);
}

View File

@ -4,13 +4,9 @@ import java.time.Duration;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -19,6 +15,8 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import jakarta.annotation.security.RolesAllowed;
/** /**
* *
* @author henkej * @author henkej
@ -26,22 +24,14 @@ import org.springframework.web.bind.annotation.RequestMethod;
*/ */
@Controller @Controller
public class DoneController { public class DoneController {
private static final Logger LOGGER = LogManager.getLogger(DoneController.class);
private final HttpServletRequest request;
@Autowired @Autowired
private IDoneService doneService; private DoneService doneService;
@Autowired
public DoneController(HttpServletRequest request) {
this.request = request;
}
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@RequestMapping(value = "/done/list") @RequestMapping(value = "/done/list")
public String getList(@ModelAttribute DoneModel doneModel, Model model) { public String getList(@ModelAttribute DoneModel doneModel, Model model, OAuth2AuthenticationToken token) {
String username = doneService.getCurrentUser(request); String username = doneService.getCurrentUser(token);
Duration maxWorkTime = Duration.ofHours(8); // TODO: to the configuration file Duration maxWorkTime = Duration.ofHours(8); // TODO: to the configuration file
LocalDate day = doneModel.getDay(); LocalDate day = doneModel.getDay();
List<DoneBean> list = doneService.getList(day, username); List<DoneBean> list = doneService.getList(day, username);
@ -61,10 +51,10 @@ public class DoneController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@GetMapping("/done/abort/{day}") @GetMapping("/done/abort/{day}")
public String abort(@PathVariable String day, Model model) { public String abort(@PathVariable String day, Model model, OAuth2AuthenticationToken token) {
DoneModel doneModel = new DoneModel(); DoneModel doneModel = new DoneModel();
doneModel.setDayString(day); doneModel.setDayString(day);
return getList(doneModel, model); return getList(doneModel, model, token);
} }
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@ -99,21 +89,21 @@ public class DoneController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@RequestMapping(value = "/done/upsert", method = RequestMethod.POST) @RequestMapping(value = "/done/upsert", method = RequestMethod.POST)
public String doUpsert(Model model, @ModelAttribute DoneBean bean) { public String doUpsert(Model model, @ModelAttribute DoneBean bean, OAuth2AuthenticationToken token) {
String username = doneService.getCurrentUser(request); String username = doneService.getCurrentUser(token);
Integer amount = doneService.doUpsert(bean, username); Integer amount = doneService.doUpsert(bean, username);
DoneModel doneModel = new DoneModel(); DoneModel doneModel = new DoneModel();
doneModel.setDay(bean.getLocalDate()); doneModel.setDay(bean.getLocalDate());
return amount.equals(1) ? getList(doneModel, model) : toItem(bean.getPk(), model); return amount.equals(1) ? getList(doneModel, model, token) : toItem(bean.getPk(), model);
} }
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@GetMapping(value = "/done/delete/{id}") @GetMapping(value = "/done/delete/{id}")
public String doDelete(@PathVariable Integer id, Model model) { public String doDelete(@PathVariable Integer id, Model model, OAuth2AuthenticationToken token) {
DoneBean bean = doneService.getBean(id); DoneBean bean = doneService.getBean(id);
Integer amount = doneService.doDelete(id); Integer amount = doneService.doDelete(id);
DoneModel doneModel = new DoneModel(); DoneModel doneModel = new DoneModel();
doneModel.setDay(bean.getLocalDate()); doneModel.setDay(bean.getLocalDate());
return amount.equals(1) ? getList(doneModel, model) : toItem(id, model); return amount.equals(1) ? getList(doneModel, model, token) : toItem(id, model);
} }
} }

View File

@ -4,13 +4,13 @@ import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext; import org.jooq.DSLContext;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -27,20 +27,29 @@ import de.jottyfan.timetrack.modules.note.NoteService;
*/ */
@Service @Service
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class DoneService implements IDoneService { public class DoneService {
private static final Logger LOGGER = LogManager.getLogger(NoteService.class); private static final Logger LOGGER = LogManager.getLogger(NoteService.class);
@Autowired @Autowired
private DSLContext dsl; private DSLContext dsl;
@Override @Autowired
public String getCurrentUser(HttpServletRequest request) { private OAuth2AuthorizedClientService security;
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request
.getAttribute(KeycloakSecurityContext.class.getName()); public String getCurrentUser(OAuth2AuthenticationToken token) {
return ksc == null ? "" : ksc.getIdToken().getPreferredUsername(); if (token != null) {
OAuth2AuthorizedClient client = security.loadAuthorizedClient(token.getAuthorizedClientRegistrationId(),
token.getName());
if (client != null) {
return client.getPrincipalName();
} else {
return "client is null";
}
} else {
return "oauth token is null";
}
} }
@Override
public List<DoneBean> getList(LocalDate day, String username) { public List<DoneBean> getList(LocalDate day, String username) {
try { try {
DoneGateway gw = new DoneGateway(dsl); DoneGateway gw = new DoneGateway(dsl);
@ -55,7 +64,6 @@ public class DoneService implements IDoneService {
} }
} }
@Override
public List<DoneBean> getWeek(LocalDate day, String username) { public List<DoneBean> getWeek(LocalDate day, String username) {
try { try {
DoneGateway gw = new DoneGateway(dsl); DoneGateway gw = new DoneGateway(dsl);
@ -70,8 +78,6 @@ public class DoneService implements IDoneService {
} }
} }
@Override
public DoneBean getBean(Integer id) { public DoneBean getBean(Integer id) {
try { try {
return new DoneGateway(dsl).getBean(id); return new DoneGateway(dsl).getBean(id);
@ -81,7 +87,6 @@ public class DoneService implements IDoneService {
} }
} }
@Override
public List<VProjectRecord> getProjects(boolean includeNull) { public List<VProjectRecord> getProjects(boolean includeNull) {
try { try {
return new DoneGateway(dsl).getAllProjects(includeNull); return new DoneGateway(dsl).getAllProjects(includeNull);
@ -91,7 +96,6 @@ public class DoneService implements IDoneService {
} }
} }
@Override
public List<VModuleRecord> getModules(boolean includeNull) { public List<VModuleRecord> getModules(boolean includeNull) {
try { try {
return new DoneGateway(dsl).getAllModules(includeNull); return new DoneGateway(dsl).getAllModules(includeNull);
@ -101,7 +105,6 @@ public class DoneService implements IDoneService {
} }
} }
@Override
public List<VJobRecord> getJobs(boolean includeNull) { public List<VJobRecord> getJobs(boolean includeNull) {
try { try {
return new DoneGateway(dsl).getAllJobs(includeNull); return new DoneGateway(dsl).getAllJobs(includeNull);
@ -111,7 +114,6 @@ public class DoneService implements IDoneService {
} }
} }
@Override
public List<VBillingRecord> getBillings(boolean includeNull) { public List<VBillingRecord> getBillings(boolean includeNull) {
try { try {
return new DoneGateway(dsl).getAllBillings(includeNull); return new DoneGateway(dsl).getAllBillings(includeNull);
@ -121,7 +123,6 @@ public class DoneService implements IDoneService {
} }
} }
@Override
public Integer doUpsert(DoneBean bean, String username) { public Integer doUpsert(DoneBean bean, String username) {
try { try {
DoneGateway gw = new DoneGateway(dsl); DoneGateway gw = new DoneGateway(dsl);

View File

@ -1,29 +0,0 @@
package de.jottyfan.timetrack.modules.done;
import java.time.LocalDate;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import de.jottyfan.timetrack.db.done.tables.records.VBillingRecord;
import de.jottyfan.timetrack.db.done.tables.records.VJobRecord;
import de.jottyfan.timetrack.db.done.tables.records.VModuleRecord;
import de.jottyfan.timetrack.db.done.tables.records.VProjectRecord;
/**
*
* @author henkej
*
*/
public interface IDoneService {
public List<DoneBean> getList(LocalDate day, String username);
public List<DoneBean> getWeek(LocalDate day, String username);
public DoneBean getBean(Integer id);
public String getCurrentUser(HttpServletRequest request);
public List<VProjectRecord> getProjects(boolean includeNull);
public List<VModuleRecord> getModules(boolean includeNull);
public List<VJobRecord> getJobs(boolean includeNull);
public List<VBillingRecord> getBillings(boolean includeNull);
public Integer doUpsert(DoneBean bean, String username);
public Integer doDelete(Integer id);
}

View File

@ -1,14 +0,0 @@
package de.jottyfan.timetrack.modules.done.job;
import de.jottyfan.timetrack.db.done.tables.records.TJobRecord;
/**
*
* @author henkej
*
*/
public interface IJobService {
public TJobRecord get(Integer id);
public Integer doUpsert(TJobRecord bean);
public Integer doDelete(Integer id);
}

View File

@ -1,10 +1,7 @@
package de.jottyfan.timetrack.modules.done.job; package de.jottyfan.timetrack.modules.done.job;
import javax.annotation.security.RolesAllowed;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -16,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import de.jottyfan.timetrack.db.done.tables.records.TJobRecord; import de.jottyfan.timetrack.db.done.tables.records.TJobRecord;
import de.jottyfan.timetrack.modules.done.DoneController; import de.jottyfan.timetrack.modules.done.DoneController;
import de.jottyfan.timetrack.modules.done.DoneModel; import de.jottyfan.timetrack.modules.done.DoneModel;
import jakarta.annotation.security.RolesAllowed;
/** /**
* *
@ -24,10 +22,8 @@ import de.jottyfan.timetrack.modules.done.DoneModel;
*/ */
@Controller @Controller
public class JobController { public class JobController {
private static final Logger LOGGER = LogManager.getLogger(JobController.class);
@Autowired @Autowired
private IJobService jobService; private JobService jobService;
@Autowired @Autowired
private DoneController doneController; private DoneController doneController;
@ -42,9 +38,9 @@ public class JobController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@RequestMapping(value = "/done/upsert/job", method = RequestMethod.POST) @RequestMapping(value = "/done/upsert/job", method = RequestMethod.POST)
public String doUpsert(Model model, @ModelAttribute TJobRecord bean) { public String doUpsert(Model model, @ModelAttribute TJobRecord bean, OAuth2AuthenticationToken token) {
Integer amount = jobService.doUpsert(bean); Integer amount = jobService.doUpsert(bean);
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toJob(bean.getPk(), model); return amount.equals(1) ? doneController.getList(new DoneModel(), model, token) : toJob(bean.getPk(), model);
} }
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@ -55,8 +51,8 @@ public class JobController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@GetMapping(value = "/done/delete/job/{id}") @GetMapping(value = "/done/delete/job/{id}")
public String doDeleteJob(@PathVariable Integer id, Model model) { public String doDeleteJob(@PathVariable Integer id, Model model, OAuth2AuthenticationToken token) {
Integer amount = jobService.doDelete(id); Integer amount = jobService.doDelete(id);
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toJob(id, model); return amount.equals(1) ? doneController.getList(new DoneModel(), model, token) : toJob(id, model);
} }
} }

View File

@ -8,18 +8,15 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.timetrack.db.done.tables.records.TJobRecord; import de.jottyfan.timetrack.db.done.tables.records.TJobRecord;
import de.jottyfan.timetrack.db.done.tables.records.TModuleRecord;
@Service @Service
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class JobService implements IJobService { public class JobService {
private static final Logger LOGGER = LogManager.getLogger(JobService.class); private static final Logger LOGGER = LogManager.getLogger(JobService.class);
@Autowired @Autowired
private DSLContext dsl; private DSLContext dsl;
@Override
public TJobRecord get(Integer id) { public TJobRecord get(Integer id) {
try { try {
return id == null ? new TJobRecord() : new JobGateway(dsl).get(id); return id == null ? new TJobRecord() : new JobGateway(dsl).get(id);
@ -29,7 +26,6 @@ public class JobService implements IJobService {
} }
} }
@Override
public Integer doUpsert(TJobRecord bean) { public Integer doUpsert(TJobRecord bean) {
try { try {
return new JobGateway(dsl).upsert(bean); return new JobGateway(dsl).upsert(bean);
@ -39,7 +35,6 @@ public class JobService implements IJobService {
} }
} }
@Override
public Integer doDelete(Integer id) { public Integer doDelete(Integer id) {
try { try {
return new JobGateway(dsl).delete(id); return new JobGateway(dsl).delete(id);

View File

@ -1,14 +0,0 @@
package de.jottyfan.timetrack.modules.done.module;
import de.jottyfan.timetrack.db.done.tables.records.TModuleRecord;
/**
*
* @author henkej
*
*/
public interface IModuleService {
public TModuleRecord get(Integer id);
public Integer doUpsert(TModuleRecord bean);
public Integer doDelete(Integer id);
}

View File

@ -1,10 +1,11 @@
package de.jottyfan.timetrack.modules.done.module; package de.jottyfan.timetrack.modules.done.module;
import javax.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -24,10 +25,9 @@ import de.jottyfan.timetrack.modules.done.DoneModel;
*/ */
@Controller @Controller
public class ModuleController { public class ModuleController {
private static final Logger LOGGER = LogManager.getLogger(ModuleController.class);
@Autowired @Autowired
private IModuleService moduleService; private ModuleService moduleService;
@Autowired @Autowired
private DoneController doneController; private DoneController doneController;
@ -42,9 +42,9 @@ public class ModuleController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@RequestMapping(value = "/done/upsert/module", method = RequestMethod.POST) @RequestMapping(value = "/done/upsert/module", method = RequestMethod.POST)
public String doUpsert(Model model, @ModelAttribute TModuleRecord bean) { public String doUpsert(Model model, @ModelAttribute TModuleRecord bean, OAuth2AuthenticationToken token) {
Integer amount = moduleService.doUpsert(bean); Integer amount = moduleService.doUpsert(bean);
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toModule(bean.getPk(), model); return amount.equals(1) ? doneController.getList(new DoneModel(), model, token) : toModule(bean.getPk(), model);
} }
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@ -55,8 +55,8 @@ public class ModuleController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@GetMapping(value = "/done/delete/module/{id}") @GetMapping(value = "/done/delete/module/{id}")
public String doDeleteModule(@PathVariable Integer id, Model model) { public String doDeleteModule(@PathVariable Integer id, Model model, OAuth2AuthenticationToken token) {
Integer amount = moduleService.doDelete(id); Integer amount = moduleService.doDelete(id);
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toModule(id, model); return amount.equals(1) ? doneController.getList(new DoneModel(), model, token) : toModule(id, model);
} }
} }

View File

@ -8,19 +8,15 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.timetrack.db.done.tables.records.TModuleRecord; import de.jottyfan.timetrack.db.done.tables.records.TModuleRecord;
import de.jottyfan.timetrack.db.done.tables.records.TProjectRecord;
import de.jottyfan.timetrack.modules.done.project.IProjectService;
@Service @Service
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class ModuleService implements IModuleService { public class ModuleService {
private static final Logger LOGGER = LogManager.getLogger(ModuleService.class); private static final Logger LOGGER = LogManager.getLogger(ModuleService.class);
@Autowired @Autowired
private DSLContext dsl; private DSLContext dsl;
@Override
public TModuleRecord get(Integer id) { public TModuleRecord get(Integer id) {
try { try {
return id == null ? new TModuleRecord() : new ModuleGateway(dsl).getModule(id); return id == null ? new TModuleRecord() : new ModuleGateway(dsl).getModule(id);
@ -30,7 +26,6 @@ public class ModuleService implements IModuleService {
} }
} }
@Override
public Integer doUpsert(TModuleRecord bean) { public Integer doUpsert(TModuleRecord bean) {
try { try {
return new ModuleGateway(dsl).upsert(bean); return new ModuleGateway(dsl).upsert(bean);
@ -40,7 +35,6 @@ public class ModuleService implements IModuleService {
} }
} }
@Override
public Integer doDelete(Integer id) { public Integer doDelete(Integer id) {
try { try {
return new ModuleGateway(dsl).delete(id); return new ModuleGateway(dsl).delete(id);

View File

@ -1,14 +0,0 @@
package de.jottyfan.timetrack.modules.done.project;
import de.jottyfan.timetrack.db.done.tables.records.TProjectRecord;
/**
*
* @author henkej
*
*/
public interface IProjectService {
public TProjectRecord get(Integer id);
public Integer doUpsert(TProjectRecord bean);
public Integer doDelete(Integer id);
}

View File

@ -1,10 +1,7 @@
package de.jottyfan.timetrack.modules.done.project; package de.jottyfan.timetrack.modules.done.project;
import javax.annotation.security.RolesAllowed;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -16,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import de.jottyfan.timetrack.db.done.tables.records.TProjectRecord; import de.jottyfan.timetrack.db.done.tables.records.TProjectRecord;
import de.jottyfan.timetrack.modules.done.DoneController; import de.jottyfan.timetrack.modules.done.DoneController;
import de.jottyfan.timetrack.modules.done.DoneModel; import de.jottyfan.timetrack.modules.done.DoneModel;
import jakarta.annotation.security.RolesAllowed;
/** /**
* *
@ -24,10 +22,8 @@ import de.jottyfan.timetrack.modules.done.DoneModel;
*/ */
@Controller @Controller
public class ProjectController { public class ProjectController {
private static final Logger LOGGER = LogManager.getLogger(ProjectController.class);
@Autowired @Autowired
private IProjectService projectService; private ProjectService projectService;
@Autowired @Autowired
private DoneController doneController; private DoneController doneController;
@ -42,9 +38,9 @@ public class ProjectController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@RequestMapping(value = "/done/upsert/project", method = RequestMethod.POST) @RequestMapping(value = "/done/upsert/project", method = RequestMethod.POST)
public String doUpsert(Model model, @ModelAttribute TProjectRecord bean) { public String doUpsert(Model model, @ModelAttribute TProjectRecord bean, OAuth2AuthenticationToken token) {
Integer amount = projectService.doUpsert(bean); Integer amount = projectService.doUpsert(bean);
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toProject(bean.getPk(), model); return amount.equals(1) ? doneController.getList(new DoneModel(), model, token) : toProject(bean.getPk(), model);
} }
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@ -55,8 +51,8 @@ public class ProjectController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@GetMapping(value = "/done/delete/project/{id}") @GetMapping(value = "/done/delete/project/{id}")
public String doDeleteProject(@PathVariable Integer id, Model model) { public String doDeleteProject(@PathVariable Integer id, Model model, OAuth2AuthenticationToken token) {
Integer amount = projectService.doDelete(id); Integer amount = projectService.doDelete(id);
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toProject(id, model); return amount.equals(1) ? doneController.getList(new DoneModel(), model, token) : toProject(id, model);
} }
} }

View File

@ -11,14 +11,12 @@ import de.jottyfan.timetrack.db.done.tables.records.TProjectRecord;
@Service @Service
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class ProjectService implements IProjectService { public class ProjectService {
private static final Logger LOGGER = LogManager.getLogger(ProjectService.class); private static final Logger LOGGER = LogManager.getLogger(ProjectService.class);
@Autowired @Autowired
private DSLContext dsl; private DSLContext dsl;
@Override
public TProjectRecord get(Integer id) { public TProjectRecord get(Integer id) {
try { try {
return id == null ? new TProjectRecord() : new ProjectGateway(dsl).getProject(id); return id == null ? new TProjectRecord() : new ProjectGateway(dsl).getProject(id);
@ -28,7 +26,6 @@ public class ProjectService implements IProjectService {
} }
} }
@Override
public Integer doUpsert(TProjectRecord bean) { public Integer doUpsert(TProjectRecord bean) {
try { try {
return new ProjectGateway(dsl).upsert(bean); return new ProjectGateway(dsl).upsert(bean);
@ -38,7 +35,6 @@ public class ProjectService implements IProjectService {
} }
} }
@Override
public Integer doDelete(Integer id) { public Integer doDelete(Integer id) {
try { try {
return new ProjectGateway(dsl).delete(id); return new ProjectGateway(dsl).delete(id);

View File

@ -1,19 +0,0 @@
package de.jottyfan.timetrack.modules.note;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
/**
*
* @author henkej
*
*/
public interface INoteService {
public List<NoteBean> getList();
public Integer doUpsert(NoteBean bean);
public Integer doDelete(Integer pk);
public Integer getAmount();
public String getCurrentUser(HttpServletRequest request);
public NoteBean getBean(Integer id);
}

View File

@ -3,11 +3,6 @@ package de.jottyfan.timetrack.modules.note;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
@ -19,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import de.jottyfan.timetrack.db.note.enums.EnumCategory; import de.jottyfan.timetrack.db.note.enums.EnumCategory;
import de.jottyfan.timetrack.db.note.enums.EnumNotetype; import de.jottyfan.timetrack.db.note.enums.EnumNotetype;
import jakarta.annotation.security.RolesAllowed;
/** /**
* *
@ -27,17 +23,9 @@ import de.jottyfan.timetrack.db.note.enums.EnumNotetype;
*/ */
@Controller @Controller
public class NoteController { public class NoteController {
private static final Logger LOGGER = LogManager.getLogger(NoteController.class);
private final HttpServletRequest request;
@Autowired @Autowired
private INoteService noteService; private NoteService noteService;
@Autowired
public NoteController(HttpServletRequest request) {
this.request = request;
}
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@RequestMapping(value = "/note/list") @RequestMapping(value = "/note/list")
@ -79,5 +67,4 @@ public class NoteController {
Integer amount = noteService.doDelete(id); Integer amount = noteService.doDelete(id);
return amount.equals(1) ? getList(model) : toItem(id, model); return amount.equals(1) ? getList(model) : toItem(id, model);
} }
} }

View File

@ -3,13 +3,13 @@ package de.jottyfan.timetrack.modules.note;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext; import org.jooq.DSLContext;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -20,19 +20,29 @@ import org.springframework.transaction.annotation.Transactional;
*/ */
@Service @Service
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class NoteService implements INoteService { public class NoteService {
private static final Logger LOGGER = LogManager.getLogger(NoteService.class); private static final Logger LOGGER = LogManager.getLogger(NoteService.class);
@Autowired @Autowired
private DSLContext dsl; private DSLContext dsl;
@Override @Autowired
public String getCurrentUser(HttpServletRequest request) { private OAuth2AuthorizedClientService security;
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
return ksc == null ? "" : ksc.getIdToken().getPreferredUsername(); public String getCurrentUser(OAuth2AuthenticationToken token) {
if (token != null) {
OAuth2AuthorizedClient client = security.loadAuthorizedClient(token.getAuthorizedClientRegistrationId(),
token.getName());
if (client != null) {
return client.getPrincipalName();
} else {
return "client is null";
}
} else {
return "oauth token is null";
}
} }
@Override
public List<NoteBean> getList() { public List<NoteBean> getList() {
try { try {
return new NoteGateway(dsl).getAll(); return new NoteGateway(dsl).getAll();
@ -42,7 +52,6 @@ public class NoteService implements INoteService {
} }
} }
@Override
public Integer doUpsert(NoteBean bean) { public Integer doUpsert(NoteBean bean) {
try { try {
NoteGateway gw = new NoteGateway(dsl); NoteGateway gw = new NoteGateway(dsl);
@ -53,7 +62,6 @@ public class NoteService implements INoteService {
} }
} }
@Override
public Integer doDelete(Integer pk) { public Integer doDelete(Integer pk) {
try { try {
return new NoteGateway(dsl).delete(pk); return new NoteGateway(dsl).delete(pk);
@ -63,12 +71,10 @@ public class NoteService implements INoteService {
} }
} }
@Override
public Integer getAmount() { public Integer getAmount() {
return getList().size(); return getList().size();
} }
@Override
public NoteBean getBean(Integer id) { public NoteBean getBean(Integer id) {
try { try {
return new NoteGateway(dsl).getBean(id); return new NoteGateway(dsl).getBean(id);

View File

@ -1,21 +1,25 @@
# jooq # jooq
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.driver-class-name = org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/timetrack # todo: export to /etc/timetrack
spring.datasource.username=timetrack spring.datasource.url = jdbc:postgresql://localhost:5432/timetrack
spring.datasource.password=timetrack spring.datasource.username = timetrack
spring.datasource.password = timetrack
# security
keycloak.url = http://localhost:8080/realms/jottyfan
keycloak.openid.url = ${keycloak.url}/protocol/openid-connect
spring.security.oauth2.client.registration.keycloak.client-id = timetrack
spring.security.oauth2.client.registration.keycloak.scope = openid
spring.security.oauth2.client.registration.keycloak.authorization-grant-type = authorization_code
# todo: export to /etc/timetrack
spring.security.oauth2.client.registration.keycloak.redirect-uri = http://localhost:8888/timetrack/login/oauth2/code/timetrack
spring.security.oauth2.client.provider.keycloak.issuer-uri = ${keycloak.url}
spring.security.oauth2.client.provider.keycloak.authorization-uri = ${keycloak.openid.url}/auth
spring.security.oauth2.client.provider.keycloak.token-uri = ${keycloak.openid.url}/token
spring.security.oauth2.client.provider.keycloak.user-info-uri = ${keycloak.openid.url}/userinfo
spring.security.oauth2.client.provider.keycloak.jwk-set-uri = ${keycloak.openid.url}/certs
spring.security.oauth2.client.provider.keycloak.user-name-attribute = preferred_username
# application # application
server.port = 8083 server:.port = 8083
server.servlet.context-path = /timetrack
server.servlet.context-path=/timetrack
# keycloak
keycloak.auth-server-url = https://www.jottyfan.de/auth
keycloak.realm = jottyfan
keycloak.resource = timetrack
keycloak.public-client = true
keycloak.security-constraints[0].authRoles[0] = timetrack_user
keycloak.security-constraints[0].securityCollections[0].patterns[0] = /*
#keycloak.credentia
keycloak.use-resource-role-mappings=true
#keycloak.bearer-only=true

View File

@ -259,8 +259,6 @@ body {
.spanlabel { .spanlabel {
display: inline-block; display: inline-block;
min-width: 128px;
max-width: 128px;
margin-top: 4px; margin-top: 4px;
margin-bottom: 4px; margin-bottom: 4px;
} }

View File

@ -1,42 +1,47 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}"> xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
<head> <head>
<title>Timetrack</title> <title>Timetrack</title>
</head> </head>
<body> <body>
<ul layout:fragment="menu"> <ul layout:fragment="menu">
</ul> </ul>
<main layout:fragment="content"> <main layout:fragment="content">
<div class="card text-dark bg-light" style="width: 256px; margin: 24px"> <div class="card text-dark bg-light" style="width: 312px; margin: 24px">
<div class="card-header"><a class="btn btn-seondary btn-bordered btn-secondaryhover" style="width: 100%" th:href="@{/done/list}">heutige Arbeitszeiten</a></div> <div class="card-header"><a class="btn btn-seondary btn-bordered btn-secondaryhover" style="width: 100%"
th:href="@{/done/list}">heutige Arbeitszeiten</a></div>
<div class="card-body"> <div class="card-body">
<div> <div class="container">
<span class="spanlabel">Start:</span> <span class="emphgreen" th:text="${sum.start}"></span> <div class="row">
<div class="col-8"><span class="spanlabel">Start:</span></div>
<div class="col-4"><span class="emphgreen" th:text="${sum.start}"></span></div>
<div class="col-8"><span class="spanlabel">Ende:</span></div>
<div class="col-4"><span class="emphgreen" th:text="${sum.end}"></span></div>
<div class="col-8"><span class="spanlabel">Arbeitszeit total:</span></div>
<div class="col-4"><span class="emphblue" th:text="${sum.total}"></span></div>
<div class="col-8"><span class="spanlabel">Pausezeit total:</span></div>
<div class="col-4"><span class="emphorange" th:text="${sum.pause}"></span></div>
<div class="col-8"><span class="spanlabel">Überstunden:</span></div>
<div class="col-4"><span class="emphred" th:text="${sum.overdue}"></span></div>
</div> </div>
<div>
<span class="spanlabel">Ende:</span> <span class="emphgreen" th:text="${sum.end}"></span>
</div>
<div>
<span class="spanlabel">Arbeitszeit total:</span> <span class="emphblue" th:text="${sum.total}"></span>
</div>
<div>
<span class="spanlabel">Pausezeit total:</span> <span class="emphorange" th:text="${sum.pause}"></span>
</div>
<div>
<span class="spanlabel">Überstunden:</span> <span class="emphred" th:text="${sum.overdue}"></span>
</div> </div>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<span th:if="${sum.getBillingTime('WP2') != '0,0 h'}"><span class="billing WP2">WP2</span><span <span th:if="${sum.getBillingTime('WP2') != '0,0 h'}"><span class="billing WP2">WP2</span><span
th:text="${sum.getBillingTime('WP2')}" class="distfat"></span></span> <span th:if="${sum.getBillingTime('WP4') != '0,0 h'}"><span th:text="${sum.getBillingTime('WP2')}" class="distfat"></span></span> <span
class="billing WP4">WP4</span><span th:text="${sum.getBillingTime('WP4')}" class="distfat"></span></span> <span th:if="${sum.getBillingTime('WP4') != '0,0 h'}"><span class="billing WP4">WP4</span><span
th:text="${sum.getBillingTime('WP4')}" class="distfat"></span></span> <span
th:if="${sum.getBillingTime('WP5') != '0,0 h'}"><span class="billing WP5">WP5</span><span th:if="${sum.getBillingTime('WP5') != '0,0 h'}"><span class="billing WP5">WP5</span><span
th:text="${sum.getBillingTime('WP5')}" class="distfat"></span></span> <span th:if="${sum.getBillingTime('TA3') != '0,0 h'}"><span th:text="${sum.getBillingTime('WP5')}" class="distfat"></span></span> <span
class="billing TA3">TA3</span><span th:text="${sum.getBillingTime('TA3')}" class="distfat"></span></span> <span class="billing">X</span><span th:if="${sum.getBillingTime('TA3') != '0,0 h'}"><span class="billing TA3">TA3</span><span
th:text="${sum.getBillingTime('TA3')}" class="distfat"></span></span> <span class="billing">X</span><span
th:text="${sum.getBillingTime(null)}" class="distfat"></span> th:text="${sum.getBillingTime(null)}" class="distfat"></span>
</div> </div>
</div> </div>
</main> </main>
</body> </body>
</html> </html>