diff --git a/pom.xml b/pom.xml index d3f8bdd..33e9e34 100644 --- a/pom.xml +++ b/pom.xml @@ -81,5 +81,16 @@ geoip2 3.0.2 + + org.springframework + spring-aop + 6.0.4 + + + org.aspectj + aspectjweaver + 1.9.19 + compile + \ No newline at end of file diff --git a/src/main/java/app/annotations/exceptions/InvalidCookie.java b/src/main/java/app/annotations/exceptions/InvalidCookie.java new file mode 100644 index 0000000..5f99bfc --- /dev/null +++ b/src/main/java/app/annotations/exceptions/InvalidCookie.java @@ -0,0 +1,4 @@ +package app.annotations.exceptions; + +public class InvalidCookie extends RuntimeException{ +} diff --git a/src/main/java/app/annotations/exceptions/LowPermition.java b/src/main/java/app/annotations/exceptions/LowPermition.java new file mode 100644 index 0000000..6c872c9 --- /dev/null +++ b/src/main/java/app/annotations/exceptions/LowPermition.java @@ -0,0 +1,4 @@ +package app.annotations.exceptions; + +public class LowPermition extends RuntimeException{ +} diff --git a/src/main/java/app/annotations/exceptions/NeedCookie.java b/src/main/java/app/annotations/exceptions/NeedCookie.java new file mode 100644 index 0000000..88599d4 --- /dev/null +++ b/src/main/java/app/annotations/exceptions/NeedCookie.java @@ -0,0 +1,4 @@ +package app.annotations.exceptions; + +public class NeedCookie extends RuntimeException{ +} diff --git a/src/main/java/app/annotations/impl/CookieAspect.java b/src/main/java/app/annotations/impl/CookieAspect.java new file mode 100644 index 0000000..ee34183 --- /dev/null +++ b/src/main/java/app/annotations/impl/CookieAspect.java @@ -0,0 +1,58 @@ +package app.annotations.impl; + +import app.annotations.exceptions.InvalidCookie; +import app.annotations.exceptions.NeedCookie; +import app.utils.SaltedCookie; +import jakarta.servlet.http.HttpServletRequest; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +@Aspect +@Configuration +public class CookieAspect { + SaltedCookie saltedCookie; + + @Autowired + public CookieAspect(SaltedCookie saltedCookie) { + this.saltedCookie = saltedCookie; + } + + @Before("@annotation(app.annotations.interfaces.NeedValidCookie) && args(request,..)") + public void before(HttpServletRequest request){ + System.out.println("check cookie"); + if(!(request instanceof HttpServletRequest)) { + throw new RuntimeException("cannot read cookie from invalid request"); + } + + if(request.getHeader("Cookie") == null) { + throw new NeedCookie(); + } + String[] rawCookieParams = request.getHeader("Cookie").split(";"); + String steam64 = ""; + String steam64_secured = ""; + + for(String rawCookie: rawCookieParams) { + if(!steam64.isEmpty() && !steam64_secured.isEmpty()) { + break; + } + if(rawCookie.contains("steam64=")) { + steam64 = rawCookie.split("=")[1]; + continue; + } + if(rawCookie.contains("steam64_secured=")) { + steam64_secured = rawCookie.split("=")[1]; + continue; + } + } + + if (steam64.isEmpty() || steam64_secured.isEmpty()) { + throw new NeedCookie(); + } + + if(!saltedCookie.Validate(steam64, steam64_secured)) { + throw new InvalidCookie(); + } + } +} diff --git a/src/main/java/app/annotations/impl/PermitionFlagAspect.java b/src/main/java/app/annotations/impl/PermitionFlagAspect.java new file mode 100644 index 0000000..e7ca472 --- /dev/null +++ b/src/main/java/app/annotations/impl/PermitionFlagAspect.java @@ -0,0 +1,57 @@ +package app.annotations.impl; + +import app.annotations.exceptions.InvalidCookie; +import app.annotations.exceptions.LowPermition; +import app.annotations.exceptions.NeedCookie; +import app.entities.db.Permition; +import app.services.ProfileService; +import jakarta.servlet.http.HttpServletRequest; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import java.util.Arrays; +import java.util.List; + +@Aspect +@Configuration +public class PermitionFlagAspect { + ProfileService profileService; + + @Autowired + public PermitionFlagAspect(ProfileService profileService) { + this.profileService = profileService; + } + + public boolean ValidateAdmin(String steam64, String flag) { + Permition permition = profileService.GetProfile(steam64, List.of("permition")).getPermition(); + if (permition == null) return false; + return permition.getFlags().contains(flag); + } + + @Before("@annotation(app.annotations.interfaces.CheckPermitionFlag) && args(request,..)") + public void before(HttpServletRequest request){ + System.out.println("check permition flag"); + if(!(request instanceof HttpServletRequest)) { + throw new RuntimeException("invalid request"); + } + + if(request.getHeader("Cookie") == null) { + throw new NeedCookie(); + } + + String steam64 = Arrays.stream(request.getHeader("Cookie").split(";")) + .filter(raw_cookie -> raw_cookie.contains("steam64=")) + .map(raw_cookie -> raw_cookie.split("=")[1]) + .findFirst().orElse(null); + + if (steam64 == null) { + throw new InvalidCookie(); + } + + if(!ValidateAdmin(steam64, "z")){ + throw new LowPermition(); + } + } +} diff --git a/src/main/java/app/annotations/interfaces/CheckPermitionFlag.java b/src/main/java/app/annotations/interfaces/CheckPermitionFlag.java new file mode 100644 index 0000000..5d3a38a --- /dev/null +++ b/src/main/java/app/annotations/interfaces/CheckPermitionFlag.java @@ -0,0 +1,13 @@ +package app.annotations.interfaces; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface CheckPermitionFlag { + public String flag() default "z"; +} diff --git a/src/main/java/app/annotations/interfaces/NeedValidCookie.java b/src/main/java/app/annotations/interfaces/NeedValidCookie.java new file mode 100644 index 0000000..326dcae --- /dev/null +++ b/src/main/java/app/annotations/interfaces/NeedValidCookie.java @@ -0,0 +1,11 @@ +package app.annotations.interfaces; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface NeedValidCookie { +} diff --git a/src/main/java/app/controllers/AdminController.java b/src/main/java/app/controllers/AdminController.java index bc48d65..096da94 100644 --- a/src/main/java/app/controllers/AdminController.java +++ b/src/main/java/app/controllers/AdminController.java @@ -1,10 +1,12 @@ package app.controllers; -import app.entities.PlayerProfile; +import app.annotations.interfaces.CheckPermitionFlag; +import app.annotations.interfaces.NeedValidCookie; import app.entities.db.Permition; import app.services.ProfileService; import app.services.ServerService; import app.utils.SaltedCookie; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -28,21 +30,15 @@ public class AdminController { this.profileService = profileService; } - public boolean ValidateAdmin(String steam64, String flag) { - Permition permition = profileService.GetProfile(steam64, List.of("permition")).getPermition(); - if (permition == null) return false; - return permition.getFlags().contains(flag); - } - @GetMapping("kick") + @NeedValidCookie + @CheckPermitionFlag(flag = "z") public ResponseEntity kickPlayer( + HttpServletRequest request, @CookieValue(value = "steam64", defaultValue = "") String steam64, - @CookieValue(value = "steam64_secured", defaultValue = "") String steam64_secured, @RequestParam(value = "steam64", required = false, defaultValue = "") String kicked_steam64, @RequestParam(value = "player_name", required = false, defaultValue = "") String player_name ) { - if(!saltedCookie.Validate(steam64, steam64_secured)) return new ResponseEntity<>(HttpStatus.FORBIDDEN); - if(!ValidateAdmin(steam64, "z")) return new ResponseEntity<>(HttpStatus.FORBIDDEN); if(kicked_steam64.isEmpty() && player_name.isEmpty()) return new ResponseEntity<>(HttpStatus.NO_CONTENT); boolean result = false; diff --git a/src/main/java/app/controllers/ProfileController.java b/src/main/java/app/controllers/ProfileController.java index 93d8f57..ebf2df6 100644 --- a/src/main/java/app/controllers/ProfileController.java +++ b/src/main/java/app/controllers/ProfileController.java @@ -1,7 +1,7 @@ package app.controllers; +import app.annotations.interfaces.NeedValidCookie; import app.services.ProfileService; -import app.entities.Stats; import app.utils.SaltedCookie; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -11,8 +11,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.List; - @RestController @RequestMapping("api/profile") public class ProfileController { @@ -27,12 +25,10 @@ public class ProfileController { } @GetMapping("/current") + @NeedValidCookie public ResponseEntity GetCurrentUser( - @CookieValue(value = "steam64", defaultValue = "") String steam64, - @CookieValue(value = "steam64_secured", defaultValue = "") String steam64_secured + @CookieValue(value = "steam64", defaultValue = "") String steam64 ){ - if(!saltedCookie.Validate(steam64, steam64_secured)) return new ResponseEntity<>(HttpStatus.FORBIDDEN); - return new ResponseEntity(profileService.GetProfile(steam64), HttpStatus.OK); } } diff --git a/src/main/java/app/exceptions/handler/GlobalExceptionAdvice.java b/src/main/java/app/exceptions/handler/GlobalExceptionAdvice.java new file mode 100644 index 0000000..527383e --- /dev/null +++ b/src/main/java/app/exceptions/handler/GlobalExceptionAdvice.java @@ -0,0 +1,27 @@ +package app.exceptions.handler; + +import app.annotations.exceptions.InvalidCookie; +import app.annotations.exceptions.LowPermition; +import app.annotations.exceptions.NeedCookie; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionAdvice { + @ExceptionHandler(NeedCookie.class) + public ResponseEntity handNeedCookie() { + return new ResponseEntity(HttpStatus.UNAUTHORIZED); + } + + @ExceptionHandler(InvalidCookie.class) + public ResponseEntity handInvalidCookie() { + return new ResponseEntity(HttpStatus.UNAUTHORIZED); + } + + @ExceptionHandler(LowPermition.class) + public ResponseEntity handLowPermition(){ + return new ResponseEntity<>(HttpStatus.FORBIDDEN); + } +}