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