diff --git a/application/pom.xml b/application/pom.xml index ed72c85..e931b24 100755 --- a/application/pom.xml +++ b/application/pom.xml @@ -22,6 +22,11 @@ webstly-core ${revision} + + de.bstly.we + webstly-dyndns + ${revision} + de.bstly.we webstly-email diff --git a/core/src/main/java/de/bstly/we/businesslogic/UserAliasManager.java b/core/src/main/java/de/bstly/we/businesslogic/UserAliasManager.java index da3b5e0..c2df266 100644 --- a/core/src/main/java/de/bstly/we/businesslogic/UserAliasManager.java +++ b/core/src/main/java/de/bstly/we/businesslogic/UserAliasManager.java @@ -15,6 +15,7 @@ import org.springframework.util.Assert; import com.google.common.collect.Lists; import de.bstly.we.model.QUserAlias; +import de.bstly.we.model.User; import de.bstly.we.model.UserAlias; import de.bstly.we.model.UserData; import de.bstly.we.repository.UserAliasRepository; @@ -110,6 +111,19 @@ public class UserAliasManager implements UserDataProvider { return userAliasRepository.findAll(PageRequest.of(page, size, sort)); } + public User getUser(String name) { + User user = userManager.getByUsername(name); + + if (user == null) { + UserAlias userAlias = getByAlias(name); + if (userAlias != null) { + user = userManager.get(userAlias.getTarget()); + } + } + + return user; + } + /* * @see de.bstly.we.businesslogic.UserDataProvider#getId() */ diff --git a/core/src/main/java/de/bstly/we/controller/UserProfileFieldController.java b/core/src/main/java/de/bstly/we/controller/UserProfileFieldController.java index 5acd0fd..b11b1f5 100644 --- a/core/src/main/java/de/bstly/we/controller/UserProfileFieldController.java +++ b/core/src/main/java/de/bstly/we/controller/UserProfileFieldController.java @@ -5,8 +5,8 @@ package de.bstly.we.controller; import java.util.List; import java.util.Optional; +import java.util.SplittableRandom; -import org.apache.commons.lang3.RandomUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; @@ -124,6 +124,7 @@ public class UserProfileFieldController extends BaseController { if (user == null) { throttleForbidden(); + return null; } List profileFields = Lists.newArrayList(); @@ -188,18 +189,21 @@ public class UserProfileFieldController extends BaseController { if (user == null) { throttleForbidden(); + return null; } UserProfileField userProfileField = userProfileFieldManager.get(user.getId(), name); if (userProfileField == null) { throttleForbidden(); + return null; } Long currentUserId = getCurrentUserId(); if (currentUserId == null && !Visibility.PUBLIC.equals(userProfileField.getVisibility())) { throttleForbidden(); + return null; } if (currentUserId != null && !currentUserId.equals(user.getId()) @@ -284,7 +288,7 @@ public class UserProfileFieldController extends BaseController { */ protected void throttleForbidden() { try { - Thread.sleep(RandomUtils.nextInt(10, 500)); + Thread.sleep(new SplittableRandom().nextInt(10, 500)); } catch (InterruptedException e) { throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); } diff --git a/core/src/main/java/de/bstly/we/controller/support/EntityResponseStatusException.java b/core/src/main/java/de/bstly/we/controller/support/EntityResponseStatusException.java index 9831a33..4e5daa7 100644 --- a/core/src/main/java/de/bstly/we/controller/support/EntityResponseStatusException.java +++ b/core/src/main/java/de/bstly/we/controller/support/EntityResponseStatusException.java @@ -5,7 +5,6 @@ package de.bstly.we.controller.support; import javax.annotation.Nullable; -import org.springframework.core.NestedExceptionUtils; import org.springframework.core.NestedRuntimeException; import org.springframework.http.HttpStatusCode; import org.springframework.util.Assert; @@ -82,8 +81,7 @@ public class EntityResponseStatusException extends NestedRuntimeException { */ @Override public String getMessage() { - String msg = this.status + (this.body != null ? " \"" + this.body + "\"" : ""); - return NestedExceptionUtils.buildMessage(msg, getCause()); + return this.status + (this.body != null ? " \"" + this.body + "\"" : ""); } } diff --git a/core/src/main/java/de/bstly/we/model/PermissionMapping.java b/core/src/main/java/de/bstly/we/model/PermissionMapping.java index 0f4d413..3de7a54 100755 --- a/core/src/main/java/de/bstly/we/model/PermissionMapping.java +++ b/core/src/main/java/de/bstly/we/model/PermissionMapping.java @@ -14,14 +14,12 @@ import jakarta.persistence.Convert; import jakarta.persistence.Converter; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; - /** * The Class PermissionMapping. */ @@ -39,8 +37,7 @@ public class PermissionMapping { private String product; @Column(name = "item", nullable = false) private Integer item; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "permission_mappings_names") private Set names; @Column(name = "addon", columnDefinition = "boolean default false") diff --git a/core/src/main/java/de/bstly/we/model/QuotaMapping.java b/core/src/main/java/de/bstly/we/model/QuotaMapping.java index 96611ed..e788d03 100644 --- a/core/src/main/java/de/bstly/we/model/QuotaMapping.java +++ b/core/src/main/java/de/bstly/we/model/QuotaMapping.java @@ -9,14 +9,12 @@ import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; - /** * The Class QuotaMapping. */ @@ -28,12 +26,10 @@ public class QuotaMapping { @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", updatable = false) private Long id; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "quota_mappings_products") private Set products; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "quota_mappings_items") private Set items; @Column(name = "name", nullable = false) diff --git a/core/src/main/java/de/bstly/we/model/SystemProperty.java b/core/src/main/java/de/bstly/we/model/SystemProperty.java index 0f3bdc8..8254e37 100755 --- a/core/src/main/java/de/bstly/we/model/SystemProperty.java +++ b/core/src/main/java/de/bstly/we/model/SystemProperty.java @@ -20,7 +20,7 @@ public class SystemProperty { @Column(name = "id") private String key; @Lob - @Column(name = "value") + @Column(name = "value", length = 100000) private String value; /** diff --git a/core/src/main/java/de/bstly/we/model/UserTotp.java b/core/src/main/java/de/bstly/we/model/UserTotp.java index 2fb04a7..3bfda5e 100644 --- a/core/src/main/java/de/bstly/we/model/UserTotp.java +++ b/core/src/main/java/de/bstly/we/model/UserTotp.java @@ -9,15 +9,13 @@ import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; - /** * The Class UserTotp. */ @@ -35,8 +33,7 @@ public class UserTotp implements SecondFactor { private String secret; @Column(name = "totp_qr_data", nullable = false) private String qrData; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "user_totps_recovery_codes") private List recoveryCodes; diff --git a/core/src/main/java/de/bstly/we/security/SecurityConfig.java b/core/src/main/java/de/bstly/we/security/SecurityConfig.java index 54f201e..dacfdf8 100755 --- a/core/src/main/java/de/bstly/we/security/SecurityConfig.java +++ b/core/src/main/java/de/bstly/we/security/SecurityConfig.java @@ -90,19 +90,22 @@ public class SecurityConfig { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // anonymous - .anonymous().authenticationFilter(localAnonymousAuthenticationFilter()).and() + .anonymous((anonymous) -> anonymous.authenticationFilter(localAnonymousAuthenticationFilter())) // session - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS) - .sessionAuthenticationStrategy(new SessionFixationProtectionStrategy()).and() - // disable deprectated xss protection - .headers().xssProtection().disable().and() + .sessionManagement((anonymous) -> anonymous.sessionCreationPolicy(SessionCreationPolicy.ALWAYS) + .sessionAuthenticationStrategy(new SessionFixationProtectionStrategy())) + // disable deprectated xss protection, x-frame + .headers((headers) -> headers.xssProtection((xssProtection) -> xssProtection.disable()) + .frameOptions((frameOptions) -> frameOptions.disable() + .referrerPolicy((referrerPolicy) -> referrerPolicy.policy(ReferrerPolicy.UNSAFE_URL)))) // form login - .formLogin().loginPage(loginUrl).usernameParameter("username").passwordParameter("password") - .loginProcessingUrl("/auth/login").defaultSuccessUrl(loginTargetUrl) - .successHandler(formAuthenticationSuccessHandler()) - .failureHandler(new SimpleUrlAuthenticationFailureHandler(loginUrl + "?error")).and() + .formLogin((formLogin) -> formLogin.loginPage(loginUrl).usernameParameter("username") + .passwordParameter("password") + .loginProcessingUrl("/auth/login").defaultSuccessUrl(loginTargetUrl) + .successHandler(formAuthenticationSuccessHandler()) + .failureHandler(new SimpleUrlAuthenticationFailureHandler(loginUrl + "?error"))) // remember me - .rememberMe().rememberMeServices(rememberMeServices()).and() + .rememberMe((rememberMe) -> rememberMe.rememberMeServices(rememberMeServices())) // form totp .addFilterBefore(formSecondFactorAuthenticationFilter(http), LocalAnonymousAuthenticationFilter.class) // rest login @@ -110,21 +113,20 @@ public class SecurityConfig { // rest totp .addFilterAfter(restSecondFactorAuthenticationFilter(http), UsernamePasswordAuthenticationFilter.class) // Logout - .logout().logoutUrl("/auth/logout").logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()) - .and() + .logout((logout) -> logout.logoutUrl("/auth/logout") + .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())) // exception - .exceptionHandling().accessDeniedHandler(localAccessDeniedHandler) - .authenticationEntryPoint(localAuthenticationEntryPoint()).and() + .exceptionHandling( + (exceptionHandling) -> exceptionHandling.accessDeniedHandler(localAccessDeniedHandler) + .authenticationEntryPoint(localAuthenticationEntryPoint())) // crsf - .csrf().disable() - // x-frame - .headers().frameOptions().disable().referrerPolicy(ReferrerPolicy.UNSAFE_URL); + .csrf((csrf) -> csrf.disable()); if (disableCors) { - http.cors().disable(); + http.cors((cors) -> cors.disable()); } else if (!allowedOriginPatterns.isEmpty()) { // cors - http.cors().configurationSource(corsConfigurationSource()); + http.cors((cors) -> cors.configurationSource(corsConfigurationSource())); } return http.build(); diff --git a/dyndns/pom.xml b/dyndns/pom.xml new file mode 100755 index 0000000..d848e5c --- /dev/null +++ b/dyndns/pom.xml @@ -0,0 +1,35 @@ + + 4.0.0 + + de.bstly.we + webstly-main + ${revision} + + + dyndns + webstly-dyndns + + + + de.bstly.we + webstly-core + ${revision} + + + + dnsjava + dnsjava + 3.5.3 + + + + + com.querydsl + querydsl-apt + ${querydsl.version} + jakarta + + + \ No newline at end of file diff --git a/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenManager.java b/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenManager.java new file mode 100644 index 0000000..f5f74a5 --- /dev/null +++ b/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenManager.java @@ -0,0 +1,128 @@ +/** + * + */ +package de.bstly.we.dyndns.businesslogic; + +import java.util.List; + +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import com.google.common.collect.Lists; + +import de.bstly.we.businesslogic.QuotaManager; +import de.bstly.we.businesslogic.UserDataProvider; +import de.bstly.we.dyndns.model.DyndnsToken; +import de.bstly.we.dyndns.repository.DyndnsTokenRepository; +import de.bstly.we.model.Quota; +import de.bstly.we.model.UserData; + +/** + * The Class DyndnsTokenManager. + */ +@Component +public class DyndnsTokenManager implements UserDataProvider { + + public static final int TOKEN_LENGTH = 32; + + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + private QuotaManager quotaManager; + @Autowired + private DyndnsTokenRepository dyndnsTokenRepository; + + /** + * Gets the. + * + * @param code the code + * @return the shortened url + */ + public DyndnsToken get(Long owner) { + return dyndnsTokenRepository.findById(owner).orElse(null); + } + + /** + * Creates the. + * + * @param owner the owner + * @param quota the quota + * @return the shortened url + */ + public DyndnsToken create(Long owner, boolean quota) { + DyndnsToken dyndnsToken = new DyndnsToken(); + String token = RandomStringUtils.random(TOKEN_LENGTH, true, true); + dyndnsToken.setOwner(owner); + dyndnsToken.setToken(token); + dyndnsToken.setTokenHash(passwordEncoder.encode(dyndnsToken.getToken())); + + dyndnsToken = dyndnsTokenRepository.save(dyndnsToken); + + if (quota) { + Quota dyndnsTokensQuota = quotaManager.get(dyndnsToken.getOwner(), DyndnsTokenQuotas.DYNDNS); + if (dyndnsTokensQuota != null) { + dyndnsTokensQuota.setValue(dyndnsTokensQuota.getValue() - 1); + quotaManager.update(dyndnsTokensQuota); + } + } + + dyndnsToken.setToken(token); + return dyndnsToken; + } + + /** + * Delete. + * + * @param dyndnsToken the shortened url + * @param quota the quota + */ + public void delete(DyndnsToken dyndnsToken, boolean quota) { + if (quota) { + Quota dyndnsTokensQuota = quotaManager.get(dyndnsToken.getOwner(), DyndnsTokenQuotas.DYNDNS); + if (dyndnsTokensQuota == null) { + dyndnsTokensQuota = quotaManager.create(dyndnsToken.getOwner(), DyndnsTokenQuotas.DYNDNS, 0, + "#", true); + } + + dyndnsTokensQuota.setValue(dyndnsTokensQuota.getValue() + 1); + quotaManager.update(dyndnsTokensQuota); + } + + dyndnsTokenRepository.delete(dyndnsToken); + } + + /* + * @see de.bstly.we.businesslogic.UserDataProvider#getId() + */ + @Override + public String getId() { + return "dyndns-tokens"; + } + + /* + * @see de.bstly.we.businesslogic.UserDataProvider#getUserData(java.lang.Long) + */ + @Override + public List getUserData(Long userId) { + List result = Lists.newArrayList(); + DyndnsToken dyndnsToken = get(userId); + if (dyndnsToken != null) { + result.add(dyndnsToken); + } + return result; + } + + /* + * @see de.bstly.we.businesslogic.UserDataProvider#purgeUserData(java.lang.Long) + */ + @Override + public void purgeUserData(Long userId) { + DyndnsToken dyndnsToken = get(userId); + if (dyndnsToken != null) { + dyndnsTokenRepository.delete(dyndnsToken); + } + } + +} diff --git a/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenPermissions.java b/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenPermissions.java new file mode 100644 index 0000000..44b4df4 --- /dev/null +++ b/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenPermissions.java @@ -0,0 +1,11 @@ +/** + * + */ +package de.bstly.we.dyndns.businesslogic; + +/** + * The Interface DyndnsTokenPermissions. + */ +public interface DyndnsTokenPermissions { + public static final String DYNDNS = "dyndns"; +} diff --git a/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenQuotas.java b/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenQuotas.java new file mode 100644 index 0000000..15edd5a --- /dev/null +++ b/dyndns/src/main/java/de/bstly/we/dyndns/businesslogic/DyndnsTokenQuotas.java @@ -0,0 +1,12 @@ +/** + * + */ +package de.bstly.we.dyndns.businesslogic; + +/** + * The Interface DyndnsTokenQuotas. + */ +public interface DyndnsTokenQuotas { + + public static final String DYNDNS = "dyndns"; +} diff --git a/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsController.java b/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsController.java new file mode 100644 index 0000000..bd7c900 --- /dev/null +++ b/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsController.java @@ -0,0 +1,104 @@ +/** + * + */ +package de.bstly.we.dyndns.controller; + +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.xbill.DNS.Name; +import org.xbill.DNS.Resolver; +import org.xbill.DNS.SimpleResolver; +import org.xbill.DNS.TSIG; +import org.xbill.DNS.Type; +import org.xbill.DNS.Update; + +import de.bstly.we.businesslogic.PermissionManager; +import de.bstly.we.businesslogic.SystemPropertyManager; +import de.bstly.we.businesslogic.UserAliasManager; +import de.bstly.we.controller.BaseController; +import de.bstly.we.controller.support.EntityResponseStatusException; +import de.bstly.we.dyndns.businesslogic.DyndnsTokenManager; +import de.bstly.we.dyndns.businesslogic.DyndnsTokenPermissions; +import de.bstly.we.dyndns.model.DyndnsToken; +import de.bstly.we.model.User; + +/** + * The Class DyndnsController. + */ +@RestController +@RequestMapping("/dyndns") +public class DyndnsController extends BaseController { + + public static final String SYSTEM_PROPERTY_DYNDNS_HOSTNAME = "dyndns.hostname"; + public static final String SYSTEM_PROPERTY_DYNDNS_KEY = "dyndns.key"; + + @Autowired + private UserAliasManager userAliasManager; + @Autowired + private DyndnsTokenManager dyndnsTokenManager; + @Autowired + private PermissionManager permissionManager; + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + private SystemPropertyManager systemPropertyManager; + + @GetMapping + public void updateDns(@RequestParam("username") String name, @RequestParam("token") String token, + @RequestParam("ip") Optional ip, @RequestParam("ipv6") Optional ipv6) { + User user = userAliasManager.getUser(name); + + if (user == null) { + throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); + } + + if (!permissionManager.hasPermission(user.getId(), DyndnsTokenPermissions.DYNDNS) + || !permissionManager.isFullUser(user.getId())) { + throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); + } + + DyndnsToken dyndnsToken = dyndnsTokenManager.get(user.getId()); + if (dyndnsToken == null) { + throw new EntityResponseStatusException(HttpStatus.PRECONDITION_FAILED); + } + + if (!passwordEncoder.matches(token, dyndnsToken.getTokenHash())) { + throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); + } + + if (ip.isEmpty() && ipv6.isEmpty()) { + throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY); + } + + try { + Name zone = Name.fromString(name + ".we.bstly.de."); + Name host = Name.fromString("host", zone); + Update update = new Update(zone); + if (ip.isPresent()) { + update.replace(host, Type.A, 3600, ip.get()); + } + if (ipv6.isPresent()) { + update.replace(host, Type.AAAA, 3600, ipv6.get()); + } + + String hostname = systemPropertyManager.get(SYSTEM_PROPERTY_DYNDNS_HOSTNAME, "127.0.0.1"); + String key = systemPropertyManager.get(SYSTEM_PROPERTY_DYNDNS_KEY, ""); + + Resolver res = new SimpleResolver(hostname); + res.setTSIGKey(new TSIG(TSIG.HMAC_SHA512, host, key)); + res.setTCP(true); + + res.send(update); + } catch (Exception e) { + throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY); + } + } + +} diff --git a/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsTokenController.java b/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsTokenController.java new file mode 100644 index 0000000..e7fd32f --- /dev/null +++ b/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsTokenController.java @@ -0,0 +1,103 @@ +/** + * + */ +package de.bstly.we.dyndns.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.bstly.we.businesslogic.PermissionManager; +import de.bstly.we.businesslogic.QuotaManager; +import de.bstly.we.controller.BaseController; +import de.bstly.we.controller.support.EntityResponseStatusException; +import de.bstly.we.dyndns.businesslogic.DyndnsTokenManager; +import de.bstly.we.dyndns.businesslogic.DyndnsTokenPermissions; +import de.bstly.we.dyndns.businesslogic.DyndnsTokenQuotas; +import de.bstly.we.dyndns.model.DyndnsToken; +import de.bstly.we.model.Quota; + +/** + * The Class DyndnsTokenController. + */ +@RestController +@RequestMapping("/dyndns/token") +public class DyndnsTokenController extends BaseController { + + @Autowired + private DyndnsTokenManager dyndnsTokenManager; + @Autowired + private PermissionManager permissionManager; + @Autowired + private QuotaManager quotaManager; + + /** + * Gets the shortened url. + * @return the shortened url + */ + @PreAuthorize("isAuthenticated()") + @GetMapping() + public DyndnsToken getDyndnsToken() { + DyndnsToken dyndnsToken = dyndnsTokenManager.get(getCurrentUserId()); + if (dyndnsToken == null) { + throw new EntityResponseStatusException(HttpStatus.NO_CONTENT); + } + + if (StringUtils.hasText(dyndnsToken.getTokenHash())) { + dyndnsToken.setTokenHash(null); + } + + return dyndnsToken; + + } + + /** + * Creates the shortened url. + * @return the shortened url + */ + @PreAuthorize("isAuthenticated()") + @PostMapping + public DyndnsToken createDyndnsToken() { + if (!permissionManager.hasPermission(getCurrentUserId(), DyndnsTokenPermissions.DYNDNS) + || !permissionManager.isFullUser(getCurrentUserId())) { + throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); + } + + DyndnsToken dyndnsToken = dyndnsTokenManager.get(getCurrentUserId()); + if (dyndnsToken != null) { + dyndnsTokenManager.delete(dyndnsToken, true); + } + + Quota shortenedUrlsQuota = quotaManager.get(getCurrentUserId(), DyndnsTokenQuotas.DYNDNS); + if (shortenedUrlsQuota == null || shortenedUrlsQuota.getValue() < 1) { + throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); + } + + return dyndnsTokenManager.create(getCurrentUserId(), true); + } + + + + /** + * Delete shortened url. + */ + @PreAuthorize("isAuthenticated()") + @DeleteMapping + public void deleteDyndnsToken() { + if (!permissionManager.hasPermission(getCurrentUserId(), DyndnsTokenPermissions.DYNDNS) + || !permissionManager.isFullUser(getCurrentUserId())) { + throw new EntityResponseStatusException(HttpStatus.FORBIDDEN); + } + + DyndnsToken dyndnsToken = dyndnsTokenManager.get(getCurrentUserId()); + if (dyndnsToken != null) { + dyndnsTokenManager.delete(dyndnsToken, true); + } + } +} diff --git a/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsTokenManagementController.java b/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsTokenManagementController.java new file mode 100644 index 0000000..6c8f5de --- /dev/null +++ b/dyndns/src/main/java/de/bstly/we/dyndns/controller/DyndnsTokenManagementController.java @@ -0,0 +1,18 @@ +/** + * + */ +package de.bstly.we.dyndns.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.bstly.we.controller.BaseController; + +/** + * The Class DyndnsTokenManagementController. + */ +@RestController +@RequestMapping("/dyndns/token/manage") +public class DyndnsTokenManagementController extends BaseController { + +} diff --git a/dyndns/src/main/java/de/bstly/we/dyndns/model/DyndnsToken.java b/dyndns/src/main/java/de/bstly/we/dyndns/model/DyndnsToken.java new file mode 100644 index 0000000..ec0912e --- /dev/null +++ b/dyndns/src/main/java/de/bstly/we/dyndns/model/DyndnsToken.java @@ -0,0 +1,88 @@ +/** + * + */ +package de.bstly.we.dyndns.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import de.bstly.we.businesslogic.support.AbstractModelEventListener; +import de.bstly.we.model.UserData; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; + +/** + * The Class DyndnsToken. + */ +@Entity +@Table(name = "dyndns_tokens") +@EntityListeners(AbstractModelEventListener.class) +public class DyndnsToken implements UserData { + + @Id + @Column(name = "owner") + private Long owner; + @JsonIgnore + @Column(name = "token", nullable = true) + private String tokenHash; + @Transient + private String token; + + /** + * Gets the owner. + * + * @return the owner + */ + public Long getOwner() { + return owner; + } + + /** + * Sets the owner. + * + * @param owner the new owner + */ + public void setOwner(Long owner) { + this.owner = owner; + } + + /** + * Gets the token hash. + * + * @return the token hash + */ + public String getTokenHash() { + return tokenHash; + } + + /** + * Sets the token hash. + * + * @param tokenHash the new token hash + */ + public void setTokenHash(String tokenHash) { + this.tokenHash = tokenHash; + } + + /** + * Gets the token. + * + * @return the token + */ + public String getToken() { + return token; + } + + /** + * Sets the token. + * + * @param token the new token + */ + public void setToken(String token) { + this.token = token; + } + +} diff --git a/dyndns/src/main/java/de/bstly/we/dyndns/repository/DyndnsTokenRepository.java b/dyndns/src/main/java/de/bstly/we/dyndns/repository/DyndnsTokenRepository.java new file mode 100755 index 0000000..b96cdde --- /dev/null +++ b/dyndns/src/main/java/de/bstly/we/dyndns/repository/DyndnsTokenRepository.java @@ -0,0 +1,18 @@ +/** + * + */ +package de.bstly.we.dyndns.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.stereotype.Repository; + +import de.bstly.we.dyndns.model.DyndnsToken; + +/** + * The Interface DyndnsTokenRepository. + */ +@Repository +public interface DyndnsTokenRepository + extends JpaRepository, QuerydslPredicateExecutor { +} diff --git a/i18n/src/main/java/de/bstly/we/i18n/model/I18n.java b/i18n/src/main/java/de/bstly/we/i18n/model/I18n.java index 61e8e4a..4fa8592 100644 --- a/i18n/src/main/java/de/bstly/we/i18n/model/I18n.java +++ b/i18n/src/main/java/de/bstly/we/i18n/model/I18n.java @@ -28,7 +28,7 @@ public class I18n { * */ @Lob - @Column(name = "label") + @Column(name = "label", length = 100000) private String label; /** diff --git a/jwt/src/main/java/de/bstly/we/jwt/model/JwtKey.java b/jwt/src/main/java/de/bstly/we/jwt/model/JwtKey.java index ab81b6e..d3c7dd3 100644 --- a/jwt/src/main/java/de/bstly/we/jwt/model/JwtKey.java +++ b/jwt/src/main/java/de/bstly/we/jwt/model/JwtKey.java @@ -5,19 +5,18 @@ package de.bstly.we.jwt.model; import java.time.Instant; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jose.jwk.KeyUse; + import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.Lob; import jakarta.persistence.Table; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.KeyType; -import com.nimbusds.jose.jwk.KeyUse; - /** * The Class JwtKey. */ diff --git a/oidc/src/main/java/de/bstly/we/oidc/model/OidcAuthorization.java b/oidc/src/main/java/de/bstly/we/oidc/model/OidcAuthorization.java index 38ad810..be47235 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/model/OidcAuthorization.java +++ b/oidc/src/main/java/de/bstly/we/oidc/model/OidcAuthorization.java @@ -9,14 +9,12 @@ import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; - /** * The Class OidcAuthorization. */ @@ -32,8 +30,7 @@ public class OidcAuthorization { private Long client; @Column(name = "subject") private Long subject; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "oidc_authorizations_scopes") private Set scopes; diff --git a/oidc/src/main/java/de/bstly/we/oidc/model/OidcClient.java b/oidc/src/main/java/de/bstly/we/oidc/model/OidcClient.java index 4702dcb..f9d8eae 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/model/OidcClient.java +++ b/oidc/src/main/java/de/bstly/we/oidc/model/OidcClient.java @@ -5,23 +5,20 @@ package de.bstly.we.oidc.model; import java.util.Set; +import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType; +import de.bstly.we.oidc.businesslogic.model.OidcClientAuthenticationMethod; import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; - -import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType; -import de.bstly.we.oidc.businesslogic.model.OidcClientAuthenticationMethod; - /** * The Class OidcClient. */ @@ -39,22 +36,18 @@ public class OidcClient { private String clientId; @Column(name = "client_secret") private String clientSecret; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "oidc_clients_methods") @Enumerated(EnumType.STRING) private Set clientAuthenticationMethods; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "oidc_clients_grant_types") @Enumerated(EnumType.STRING) private Set authorizationGrantTypes; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "oidc_clients_redirect_uris") private Set redirectUris; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "oidc_clients_scopes") private Set scopes; @Column(name = "token_lifetime") diff --git a/oidc/src/main/java/de/bstly/we/oidc/model/OidcToken.java b/oidc/src/main/java/de/bstly/we/oidc/model/OidcToken.java index 1ed8a1c..8b91fad 100644 --- a/oidc/src/main/java/de/bstly/we/oidc/model/OidcToken.java +++ b/oidc/src/main/java/de/bstly/we/oidc/model/OidcToken.java @@ -6,19 +6,17 @@ package de.bstly.we.oidc.model; import java.time.Instant; import java.util.Set; +import com.fasterxml.jackson.annotation.JsonIgnore; + import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; - -import com.fasterxml.jackson.annotation.JsonIgnore; - /** * The Class OidcToken. */ @@ -47,8 +45,7 @@ public class OidcToken { @Column(name = "id_token", length = 4000) private String idToken; @JsonIgnore - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) private Set scopes; /** diff --git a/partey/src/main/java/de/bstly/we/partey/model/ParteyMap.java b/partey/src/main/java/de/bstly/we/partey/model/ParteyMap.java index 15f4848..2bc4065 100644 --- a/partey/src/main/java/de/bstly/we/partey/model/ParteyMap.java +++ b/partey/src/main/java/de/bstly/we/partey/model/ParteyMap.java @@ -5,19 +5,17 @@ package de.bstly.we.partey.model; import java.util.List; +import com.google.common.collect.Lists; + import jakarta.persistence.CollectionTable; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; - -import com.google.common.collect.Lists; - /** * The Class ParteyMap. */ @@ -29,8 +27,7 @@ public class ParteyMap { private String id; @Enumerated(EnumType.STRING) private GameRoomPolicyTypes policyType = GameRoomPolicyTypes.MEMBERS_ONLY_POLICY; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "partey_maps_tags") private List tags = Lists.newArrayList(); diff --git a/partey/src/main/java/de/bstly/we/partey/model/ParteyUserTextures.java b/partey/src/main/java/de/bstly/we/partey/model/ParteyUserTextures.java index 22d098d..d3e8bda 100644 --- a/partey/src/main/java/de/bstly/we/partey/model/ParteyUserTextures.java +++ b/partey/src/main/java/de/bstly/we/partey/model/ParteyUserTextures.java @@ -5,20 +5,17 @@ package de.bstly.we.partey.model; import java.util.List; +import com.google.common.collect.Lists; + +import de.bstly.we.model.UserData; import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; - -import com.google.common.collect.Lists; - -import de.bstly.we.model.UserData; - /** * The Class ParteyUserTextures. */ @@ -30,8 +27,7 @@ public class ParteyUserTextures implements UserData { private Long target; @Column(name = "username", nullable = false) private String username; - @ElementCollection - @LazyCollection(LazyCollectionOption.FALSE) + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "partey_user_textures_list") private List textures = Lists.newArrayList(); diff --git a/pom.xml b/pom.xml index 63bc885..c6fbb1e 100755 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,7 @@ UTF-8 17 5.0.0 - 9.37.1 - 6.4.0.Final + 9.37.3 3.0.0-SNAPSHOT @@ -29,6 +28,7 @@ application borrow core + dyndns email i18n invite