Alles mögliche
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
This commit is contained in:
27
src/main/java/de/oaa/xxx/config/CookieFactory.java
Normal file
27
src/main/java/de/oaa/xxx/config/CookieFactory.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package de.oaa.xxx.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Component
|
||||
public class CookieFactory {
|
||||
|
||||
private final boolean secure;
|
||||
|
||||
public CookieFactory(@Value("${app.cookie.secure:true}") boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public ResponseCookie jwtCookie(String token, Duration maxAge) {
|
||||
return ResponseCookie.from("jwt", token)
|
||||
.httpOnly(true)
|
||||
.secure(secure)
|
||||
.sameSite("Strict")
|
||||
.path("/")
|
||||
.maxAge(maxAge)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,11 @@ import java.util.Collections;
|
||||
public class JwtFilter extends OncePerRequestFilter {
|
||||
|
||||
private final JwtService jwtService;
|
||||
private final TokenBlacklistService tokenBlacklist;
|
||||
|
||||
public JwtFilter(JwtService jwtService) {
|
||||
public JwtFilter(JwtService jwtService, TokenBlacklistService tokenBlacklist) {
|
||||
this.jwtService = jwtService;
|
||||
this.tokenBlacklist = tokenBlacklist;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,10 +34,13 @@ public class JwtFilter extends OncePerRequestFilter {
|
||||
if ("jwt".equals(cookie.getName())) {
|
||||
try {
|
||||
Claims claims = jwtService.validateAndGetClaims(cookie.getValue());
|
||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
|
||||
claims.getSubject(), null, Collections.emptyList()
|
||||
);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
String jti = claims.getId();
|
||||
if (jti == null || !tokenBlacklist.isBlacklisted(jti)) {
|
||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
|
||||
claims.getSubject(), null, Collections.emptyList()
|
||||
);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ungültiger oder abgelaufener Token – ohne Authentifizierung weiter
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class JwtService {
|
||||
@@ -33,12 +34,17 @@ public class JwtService {
|
||||
return Jwts.builder()
|
||||
.subject(email)
|
||||
.claim("name", name)
|
||||
.id(UUID.randomUUID().toString())
|
||||
.issuedAt(new Date())
|
||||
.expiration(new Date(System.currentTimeMillis() + EXPIRATION_MS))
|
||||
.signWith(privateKey)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public long getExpirationMs() {
|
||||
return EXPIRATION_MS;
|
||||
}
|
||||
|
||||
public Claims validateAndGetClaims(String token) {
|
||||
return Jwts.parser()
|
||||
.verifyWith(publicKey)
|
||||
|
||||
70
src/main/java/de/oaa/xxx/config/RateLimitFilter.java
Normal file
70
src/main/java/de/oaa/xxx/config/RateLimitFilter.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package de.oaa.xxx.config;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Component
|
||||
public class RateLimitFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final int MAX_REQUESTS = 10;
|
||||
private static final long WINDOW_MS = 60_000;
|
||||
|
||||
private static final String[] RATE_LIMITED_PATHS = {
|
||||
"/login", "/registration", "/password-reset"
|
||||
};
|
||||
|
||||
private record Window(AtomicInteger count, long startMs) {}
|
||||
private final Map<String, Window> windows = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
String path = request.getRequestURI();
|
||||
boolean isRateLimited = false;
|
||||
for (String p : RATE_LIMITED_PATHS) {
|
||||
if (path.equals(p) || path.startsWith(p + "/")) {
|
||||
isRateLimited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRateLimited) {
|
||||
String ip = getClientIp(request);
|
||||
String key = ip + ":" + path;
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
Window window = windows.compute(key, (k, w) -> {
|
||||
if (w == null || now - w.startMs() > WINDOW_MS) {
|
||||
return new Window(new AtomicInteger(1), now);
|
||||
}
|
||||
w.count().incrementAndGet();
|
||||
return w;
|
||||
});
|
||||
|
||||
if (window.count().get() > MAX_REQUESTS) {
|
||||
response.setStatus(429);
|
||||
response.getWriter().write("Too many requests");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
String xff = request.getHeader("X-Forwarded-For");
|
||||
if (xff != null && !xff.isBlank()) {
|
||||
return xff.split(",")[0].trim();
|
||||
}
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,10 @@ public class SecurityConfig {
|
||||
.requestMatchers("/games/chastity/joinlock.html").authenticated()
|
||||
.requestMatchers("/community/benachrichtigungen.html").authenticated()
|
||||
.requestMatchers("/community/abonnements.html").authenticated()
|
||||
.requestMatchers("/dating/dating.html").authenticated()
|
||||
.requestMatchers("/dating/besucher.html").authenticated()
|
||||
.requestMatchers("/dating/likes.html").authenticated()
|
||||
.requestMatchers("/dating/matches.html").authenticated()
|
||||
.requestMatchers("/community/locations.html").authenticated()
|
||||
.requestMatchers("/community/location-detail.html").authenticated()
|
||||
.requestMatchers("/community/events.html").authenticated()
|
||||
@@ -80,7 +84,6 @@ public class SecurityConfig {
|
||||
.requestMatchers("/notifications/**").authenticated()
|
||||
.requestMatchers("/events/**").authenticated()
|
||||
.requestMatchers("/*.html").permitAll()
|
||||
.requestMatchers("/**/*.html").permitAll()
|
||||
.requestMatchers("/help/*.html").permitAll()
|
||||
.requestMatchers("/css/**").permitAll()
|
||||
.requestMatchers("/js/**").permitAll()
|
||||
|
||||
34
src/main/java/de/oaa/xxx/config/TokenBlacklistService.java
Normal file
34
src/main/java/de/oaa/xxx/config/TokenBlacklistService.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package de.oaa.xxx.config;
|
||||
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Service
|
||||
public class TokenBlacklistService {
|
||||
|
||||
// jti -> Ablaufzeit in ms
|
||||
private final Map<String, Long> blacklist = new ConcurrentHashMap<>();
|
||||
|
||||
public void blacklist(String jti, long expiryMs) {
|
||||
blacklist.put(jti, expiryMs);
|
||||
}
|
||||
|
||||
public boolean isBlacklisted(String jti) {
|
||||
Long expiry = blacklist.get(jti);
|
||||
if (expiry == null) return false;
|
||||
if (System.currentTimeMillis() > expiry) {
|
||||
blacklist.remove(jti);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 3_600_000)
|
||||
public void cleanup() {
|
||||
long now = System.currentTimeMillis();
|
||||
blacklist.entrySet().removeIf(e -> now > e.getValue());
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,29 @@
|
||||
package de.oaa.xxx.emailchange;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import de.oaa.xxx.config.CookieFactory;
|
||||
import de.oaa.xxx.mail.Email;
|
||||
import de.oaa.xxx.mail.MailService;
|
||||
import de.oaa.xxx.mail.MailTemplateService;
|
||||
import de.oaa.xxx.registration.RegistrationRepository;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/email-change")
|
||||
@@ -34,17 +40,20 @@ public class EmailChangeController {
|
||||
private final RegistrationRepository registrationRepository;
|
||||
private final MailService mailService;
|
||||
private final MailTemplateService mailTemplateService;
|
||||
private final CookieFactory cookieFactory;
|
||||
|
||||
public EmailChangeController(EmailChangeRepository emailChangeRepository,
|
||||
UserRepository userRepository,
|
||||
RegistrationRepository registrationRepository,
|
||||
MailService mailService,
|
||||
MailTemplateService mailTemplateService) {
|
||||
MailTemplateService mailTemplateService,
|
||||
CookieFactory cookieFactory) {
|
||||
this.emailChangeRepository = emailChangeRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.registrationRepository = registrationRepository;
|
||||
this.mailService = mailService;
|
||||
this.mailTemplateService = mailTemplateService;
|
||||
this.cookieFactory = cookieFactory;
|
||||
}
|
||||
|
||||
record EmailChangeRequest(String newEmail) {}
|
||||
@@ -113,13 +122,7 @@ public class EmailChangeController {
|
||||
emailChangeRepository.delete(entity.get());
|
||||
|
||||
// Clear JWT cookie so user must log in with new email
|
||||
ResponseCookie cookie = ResponseCookie.from("jwt", "")
|
||||
.httpOnly(true)
|
||||
.sameSite("Strict")
|
||||
.path("/")
|
||||
.maxAge(0)
|
||||
.build();
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookieFactory.jwtCookie("", java.time.Duration.ZERO).toString());
|
||||
response.sendRedirect("/login.html?emailChanged=1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +264,8 @@ public class CardLockController {
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(Map.of("lockId", lock.getLockId().toString(), "unlockCode", lock.getUnlockCode(),
|
||||
return ResponseEntity.ok(Map.of("lockId", lock.getLockId().toString(),
|
||||
"unlockCode", lock.getUnlockCode() != null ? lock.getUnlockCode() : "",
|
||||
"keyholderPending", keyholderPending));
|
||||
}
|
||||
|
||||
@@ -408,6 +409,26 @@ public class CardLockController {
|
||||
return ResponseEntity.ok(Map.of("lockId", activeLockId.get().toString()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/mylock")
|
||||
@Transactional
|
||||
public ResponseEntity<Void> deleteMyActiveLock(Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
var lockOpt = cardlockRepository.findByLockee(myId).stream()
|
||||
.filter(l -> l.getStartTime() != null && l.getUnlockTime() == null)
|
||||
.findFirst();
|
||||
if (lockOpt.isEmpty())
|
||||
return ResponseEntity.noContent().build();
|
||||
var l = lockOpt.get();
|
||||
CardLockService service = cardLockServiceFactory.create(l);
|
||||
service.unlock(l.getUnlockCode());
|
||||
var verifications = verificationRepository.findByLockId(l.getLockId());
|
||||
verifications.forEach(v -> verificationVoteRepository.deleteAllByVerificationId(v.getDisplayId()));
|
||||
verificationRepository.deleteAll(verifications);
|
||||
invitationRepository.deleteByLockId(l.getLockId());
|
||||
cardlockRepository.deleteById(l.getLockId());
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@GetMapping("/cardlock/{lockId}")
|
||||
public ResponseEntity<Map<String, Object>> getLock(@PathVariable("lockId") UUID lockId, Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
@@ -189,6 +189,14 @@ public class TimeLockController {
|
||||
|
||||
// ── State abrufen ────────────────────────────────────────────────────────────
|
||||
|
||||
@GetMapping("/timelock/mylock")
|
||||
public ResponseEntity<Map<String, Object>> getMyActiveTimeLock(Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
return timeLockRepository.findFirstByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(myId)
|
||||
.map(l -> ResponseEntity.ok(Map.<String, Object>of("lockId", l.getLockId().toString())))
|
||||
.orElse(ResponseEntity.noContent().build());
|
||||
}
|
||||
|
||||
@GetMapping("/timelock/{lockId}")
|
||||
@Transactional
|
||||
public ResponseEntity<Map<String, Object>> getTimeLock(@PathVariable("lockId") UUID lockId, Principal principal) {
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package de.oaa.xxx.games.chastity.ttlock;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import de.oaa.xxx.games.chastity.ttlock.TTLockService.TTLockDetailResponse;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/ttlock")
|
||||
public class TTLockTest {
|
||||
|
||||
private final TTAuthService auth;
|
||||
private final TTLockService lock;
|
||||
|
||||
private String clientId = "6e5077a84b6a4e1ba0fb6a8da21c6417";
|
||||
private String clientSecret = "a2c1d68c7905d52584fc29028937db11";
|
||||
private String username= "mario.stoermer@proton.me";
|
||||
private String password = "knall666.Halla";
|
||||
private int lockId = 30158446;
|
||||
|
||||
public TTLockTest(TTAuthService auth, TTLockService lock) {
|
||||
this.auth = auth;
|
||||
this.lock = lock;
|
||||
}
|
||||
|
||||
@GetMapping("/details")
|
||||
public ResponseEntity<TTLockDetailResponse> details() {
|
||||
String md5Hex = org.apache.commons.codec.digest.DigestUtils.md5Hex(password).toLowerCase();
|
||||
String token = auth.getAccessToken(clientId, clientSecret, username, md5Hex);
|
||||
return ResponseEntity.ok(lock.getLockDetail(clientId, token, lockId));
|
||||
}
|
||||
|
||||
@GetMapping("/add/{pin}")
|
||||
public ResponseEntity<Integer> add(@PathVariable String pin) {
|
||||
String md5Hex = org.apache.commons.codec.digest.DigestUtils.md5Hex(password).toLowerCase();
|
||||
String token = auth.getAccessToken(clientId, clientSecret, username, md5Hex);
|
||||
return ResponseEntity.ok(lock.addCustomPasscode(clientId, token, lockId, pin));
|
||||
}
|
||||
|
||||
@GetMapping("/delete/{id}")
|
||||
public ResponseEntity<String> remove(@PathVariable Integer id) {
|
||||
String md5Hex = org.apache.commons.codec.digest.DigestUtils.md5Hex(password).toLowerCase();
|
||||
String token = auth.getAccessToken(clientId, clientSecret, username, md5Hex);
|
||||
return ResponseEntity.ok(lock.deleteCustomPasscode(clientId, token, lockId, id));
|
||||
}
|
||||
|
||||
@GetMapping("/delete/all")
|
||||
public void removeAll() {
|
||||
String md5Hex = org.apache.commons.codec.digest.DigestUtils.md5Hex(password).toLowerCase();
|
||||
String token = auth.getAccessToken(clientId, clientSecret, username, md5Hex);
|
||||
lock.findAndDeleteLocksByName(clientId, token, lockId);
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
1-unlock by app
|
||||
|
||||
4-unlock by passcode
|
||||
|
||||
5-Rise the lock (for parking lock)
|
||||
|
||||
6-Lower the lock (for parking lock)
|
||||
|
||||
7-unlock by IC card
|
||||
|
||||
8-unlock by fingerprint
|
||||
|
||||
9-unlock by wrist strap
|
||||
|
||||
10-unlock by Mechanical key
|
||||
|
||||
11-lock by app
|
||||
|
||||
12-unlock by gateway
|
||||
|
||||
29-apply some force on the Lock
|
||||
|
||||
30-Door sensor closed
|
||||
|
||||
31-Door sensor open
|
||||
|
||||
32-open from inside
|
||||
|
||||
33-lock by fingerprint
|
||||
|
||||
34-lock by passcode
|
||||
|
||||
35-lock by IC card
|
||||
|
||||
36-lock by Mechanical key
|
||||
|
||||
37-Use APP button to control the lock (rise, fall, stop, lock), mostly used for roller shutter door
|
||||
|
||||
42-received new local mail
|
||||
|
||||
43-received new other cities' mail
|
||||
|
||||
44-Tamper alert
|
||||
|
||||
45-Auto Lock
|
||||
|
||||
46-unlock by unlock key
|
||||
|
||||
47-lock by lock key
|
||||
|
||||
48-System locked ( Caused by, for example: Using INVALID Passcode/Fingerprint/Card several times)
|
||||
|
||||
49-unlock by hotel card
|
||||
|
||||
50-Unlocked due to the high temperature
|
||||
|
||||
51-Try to unlock with a deleted card
|
||||
|
||||
52-Dead lock with APP
|
||||
|
||||
53-Dead lock with passcode
|
||||
|
||||
54-The car left (for parking lock)
|
||||
|
||||
55-Use remote control lock or unlock lock
|
||||
|
||||
57-Unlock with QR code success
|
||||
|
||||
58-Unlock with QR code failed, it's expired
|
||||
|
||||
59-Double locked
|
||||
|
||||
60-Cancel double lock
|
||||
|
||||
61-Lock with QR code success
|
||||
|
||||
62-Lock with QR code failed, the lock is double locked
|
||||
|
||||
63-Auto unlock at passage mode
|
||||
|
||||
64-Door unclosed alarm
|
||||
|
||||
65-Failed to unlock
|
||||
|
||||
66-Failed to lock
|
||||
|
||||
67-Face unlock success
|
||||
|
||||
68-Face unlock failed - door locked from inside
|
||||
|
||||
69-Lock with face
|
||||
|
||||
71-Face unlock failed - expired or ineffective
|
||||
|
||||
75-Unlocked by App granting
|
||||
|
||||
76-Unlocked by remote granting
|
||||
|
||||
77-Dual authentication Bluetooth unlock verification success, waiting for second user
|
||||
|
||||
78-Dual authentication password unlock verification success, waiting for second user
|
||||
|
||||
79-Dual authentication fingerprint unlock verification success, waiting for second user
|
||||
|
||||
80-Dual authentication IC card unlock verification success, waiting for second user
|
||||
|
||||
81-Dual authentication face card unlock verification success, waiting for second user
|
||||
|
||||
82-Dual authentication wireless key unlock verification success, waiting for second user
|
||||
|
||||
83-Dual authentication palm vein unlock verification success, waiting for second user
|
||||
|
||||
84-Palm vein unlock success
|
||||
|
||||
85-Palm vein unlock success
|
||||
|
||||
86-Lock with palm vein
|
||||
|
||||
88-Palm vein unlock failed - expired or ineffective
|
||||
|
||||
92-Administrator password to unlock
|
||||
@@ -449,9 +449,9 @@ public class LocationEventController {
|
||||
.map(e -> {
|
||||
var loc = locationById.get(e.getLocationId());
|
||||
String locName = loc != null ? loc.getName() : "";
|
||||
double dist = (loc != null && loc.getLat() != null && loc.getLon() != null)
|
||||
? Math.round(LocationController.haversineKm(refLat, refLon, loc.getLat(), loc.getLon()) * 10.0) / 10.0
|
||||
: -1;
|
||||
// double dist = (loc != null && loc.getLat() != null && loc.getLon() != null)
|
||||
// ? Math.round(LocationController.haversineKm(refLat, refLon, loc.getLat(), loc.getLon()) * 10.0) / 10.0
|
||||
// : -1;
|
||||
return toPreview(e, locName, refLat, refLon, myId);
|
||||
})
|
||||
.toList();
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package de.oaa.xxx.user;
|
||||
|
||||
import de.oaa.xxx.admin.AdminRepository;
|
||||
import de.oaa.xxx.config.CookieFactory;
|
||||
import de.oaa.xxx.config.JwtService;
|
||||
import de.oaa.xxx.config.TokenBlacklistService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -18,6 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@@ -33,13 +36,17 @@ public class LoginController {
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final AdminRepository adminRepository;
|
||||
private final UserService userService;
|
||||
private final TokenBlacklistService tokenBlacklist;
|
||||
private final CookieFactory cookieFactory;
|
||||
|
||||
public LoginController(UserRepository userRepository, JwtService jwtService, PasswordEncoder passwordEncoder, AdminRepository adminRepository, UserService userService) {
|
||||
public LoginController(UserRepository userRepository, JwtService jwtService, PasswordEncoder passwordEncoder, AdminRepository adminRepository, UserService userService, TokenBlacklistService tokenBlacklist, CookieFactory cookieFactory) {
|
||||
this.userRepository = userRepository;
|
||||
this.jwtService = jwtService;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.adminRepository = adminRepository;
|
||||
this.userService = userService;
|
||||
this.tokenBlacklist = tokenBlacklist;
|
||||
this.cookieFactory = cookieFactory;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@@ -49,13 +56,7 @@ public class LoginController {
|
||||
UserEntity user = userOpt.get();
|
||||
LOGGER.info("User erfolgreich angemeldet: {}", request.email());
|
||||
String token = jwtService.generateToken(user.getEmail(), user.getName());
|
||||
ResponseCookie cookie = ResponseCookie.from("jwt", token)
|
||||
.httpOnly(true)
|
||||
.sameSite("Strict")
|
||||
.path("/")
|
||||
.maxAge(Duration.ofHours(24))
|
||||
.build();
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookieFactory.jwtCookie(token, Duration.ofHours(24)).toString());
|
||||
User u = user.toUser();
|
||||
u.setAdmin(adminRepository.existsByUserId(user.getUserId()));
|
||||
return ResponseEntity.ok(u);
|
||||
@@ -76,14 +77,22 @@ public class LoginController {
|
||||
}
|
||||
|
||||
@GetMapping("/logout")
|
||||
public void logout(HttpServletResponse response) throws java.io.IOException {
|
||||
ResponseCookie cookie = ResponseCookie.from("jwt", "")
|
||||
.httpOnly(true)
|
||||
.sameSite("Strict")
|
||||
.path("/")
|
||||
.maxAge(0)
|
||||
.build();
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException {
|
||||
if (request.getCookies() != null) {
|
||||
Arrays.stream(request.getCookies())
|
||||
.filter(c -> "jwt".equals(c.getName()))
|
||||
.findFirst()
|
||||
.ifPresent(c -> {
|
||||
try {
|
||||
var claims = jwtService.validateAndGetClaims(c.getValue());
|
||||
String jti = claims.getId();
|
||||
if (jti != null) {
|
||||
tokenBlacklist.blacklist(jti, claims.getExpiration().getTime());
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
});
|
||||
}
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookieFactory.jwtCookie("", Duration.ZERO).toString());
|
||||
response.sendRedirect("/");
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public class User {
|
||||
private boolean admin;
|
||||
private String profilePicture;
|
||||
private LocalDate geburtsdatum;
|
||||
private LocalDate registrierungsdatum;
|
||||
private Integer groesse;
|
||||
private Integer gewicht;
|
||||
private Geschlecht geschlecht;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.oaa.xxx.user;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
@@ -13,8 +14,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import de.oaa.xxx.config.CookieFactory;
|
||||
import de.oaa.xxx.games.bdsm.entity.BdsmDefaultsEntity;
|
||||
import de.oaa.xxx.games.bdsm.repository.BdsmDefaultsRepository;
|
||||
import de.oaa.xxx.games.chastity.common.BaseLockRepository;
|
||||
@@ -39,9 +41,6 @@ import de.oaa.xxx.registration.RegistrationRepository;
|
||||
import de.oaa.xxx.social.entity.MessageCause;
|
||||
import de.oaa.xxx.social.entity.NotificationPreferenceEntity;
|
||||
import de.oaa.xxx.social.repository.NotificationPreferenceRepository;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
@@ -60,6 +59,7 @@ public class UserController {
|
||||
private final TTLockService ttLockService;
|
||||
private final BaseLockRepository baseLockRepository;
|
||||
private final BaseLockTemplateRepository baseLockTemplateRepository;
|
||||
private final CookieFactory cookieFactory;
|
||||
|
||||
public UserController(UserRepository userRepository,
|
||||
RegistrationRepository registrationRepository,
|
||||
@@ -71,7 +71,8 @@ public class UserController {
|
||||
TTAuthService ttAuthService,
|
||||
TTLockService ttLockService,
|
||||
BaseLockRepository baseLockRepository,
|
||||
BaseLockTemplateRepository baseLockTemplateRepository) {
|
||||
BaseLockTemplateRepository baseLockTemplateRepository,
|
||||
CookieFactory cookieFactory) {
|
||||
this.userRepository = userRepository;
|
||||
this.registrationRepository = registrationRepository;
|
||||
this.notificationPreferenceRepository = notificationPreferenceRepository;
|
||||
@@ -83,6 +84,7 @@ public class UserController {
|
||||
this.ttLockService = ttLockService;
|
||||
this.baseLockRepository = baseLockRepository;
|
||||
this.baseLockTemplateRepository = baseLockTemplateRepository;
|
||||
this.cookieFactory = cookieFactory;
|
||||
}
|
||||
|
||||
record ProfilePictureRequest(String picture, String pictureHq) {}
|
||||
@@ -348,14 +350,8 @@ public class UserController {
|
||||
|
||||
userService.deleteAccount(userId, email);
|
||||
|
||||
ResponseCookie cookie = ResponseCookie.from("jwt", "")
|
||||
.httpOnly(true)
|
||||
.sameSite("Strict")
|
||||
.path("/")
|
||||
.maxAge(0)
|
||||
.build();
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.SET_COOKIE, cookie.toString())
|
||||
.header(HttpHeaders.SET_COOKIE, cookieFactory.jwtCookie("", java.time.Duration.ZERO).toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -501,6 +497,31 @@ public class UserController {
|
||||
}
|
||||
}
|
||||
|
||||
record NewMemberDto(UUID userId, String name, String profilePicture,
|
||||
Integer alter, String geschlecht, String neigung,
|
||||
String datingStadt, String beschreibung) {}
|
||||
|
||||
@GetMapping("/new-members")
|
||||
public ResponseEntity<List<NewMemberDto>> getNewMembers(Principal principal) {
|
||||
UUID myId = principal != null ? userService.requireUser(principal).getUserId() : null;
|
||||
LocalDate since = LocalDate.now().minusDays(14);
|
||||
List<NewMemberDto> result = userRepository
|
||||
.findByRegistrierungsdatumAfterOrderByRegistrierungsdatumDesc(since)
|
||||
.stream()
|
||||
.filter(u -> !u.getUserId().equals(myId))
|
||||
.map(u -> new NewMemberDto(
|
||||
u.getUserId(),
|
||||
u.getName(),
|
||||
u.getProfilePictureHq() != null ? u.getProfilePictureHq() : u.getProfilePicture(),
|
||||
u.getAlter(),
|
||||
u.getGeschlecht() != null ? u.getGeschlecht().name() : null,
|
||||
u.getNeigung() != null ? u.getNeigung().name() : null,
|
||||
u.getDatingStadt(),
|
||||
u.getBeschreibung()))
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
record LocationFilterRequest(String filterCity, Double filterLat, Double filterLon, Integer filterMaxDistKm) {}
|
||||
|
||||
@PutMapping("/me/location-filter")
|
||||
|
||||
@@ -39,6 +39,9 @@ public class UserEntity {
|
||||
@Column
|
||||
private LocalDate geburtsdatum;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDate registrierungsdatum;
|
||||
|
||||
@Column
|
||||
private Integer groesse;
|
||||
|
||||
@@ -163,6 +166,7 @@ public class UserEntity {
|
||||
user.setUserId(userId);
|
||||
user.setProfilePicture(profilePicture);
|
||||
user.setGeburtsdatum(geburtsdatum);
|
||||
user.setRegistrierungsdatum(registrierungsdatum);
|
||||
user.setGroesse(groesse);
|
||||
user.setGewicht(gewicht);
|
||||
user.setGeschlecht(geschlecht);
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.oaa.xxx.user;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
@@ -12,4 +13,5 @@ public interface UserRepository extends JpaRepository<UserEntity, UUID> {
|
||||
Optional<UserEntity> findByName(String name);
|
||||
List<UserEntity> findByNameContainingIgnoreCase(String name);
|
||||
List<UserEntity> findByDatingAktiv(boolean datingAktiv);
|
||||
List<UserEntity> findByRegistrierungsdatumAfterOrderByRegistrierungsdatumDesc(LocalDate since);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -220,6 +221,7 @@ public class UserService {
|
||||
entity.setName(registration.getName());
|
||||
entity.setPassword(registration.getPassword());
|
||||
entity.setGeburtsdatum(registration.getGeburtsdatum());
|
||||
entity.setRegistrierungsdatum(LocalDate.now());
|
||||
userRepository.save(entity);
|
||||
|
||||
for (MessageCause cause : MessageCause.values()) {
|
||||
|
||||
Reference in New Issue
Block a user