commit 5a7951a131fd0ffa9bafa65c9d9e1193e5755d73 Author: Akito123321 Date: Wed Jun 5 01:19:08 2024 +0200 first commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..28df922 --- /dev/null +++ b/.classpath @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..1e3952b --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + valoStratsBackend + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..cdfe4f1 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..5e4ec05 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.methodParameters=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6cf3334 --- /dev/null +++ b/pom.xml @@ -0,0 +1,90 @@ + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.4 + + + me.akito123321.valoStratsBackend + valoStratsBackend + 0.0.1-SNAPSHOT + + 17 + + + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + + com.squareup.retrofit2 + retrofit + 2.10.0 + + + + com.google.code.gson + gson + + + + + com.squareup.retrofit2 + converter-gson + 2.10.0 + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.mariadb.jdbc + mariadb-java-client + + + com.sigpwned + opengraph4j + 0.0.0 + + + + org.jsoup + jsoup + 1.17.2 + + + \ No newline at end of file diff --git a/src/main/java/me/akito123321/valoStrats/Config.java b/src/main/java/me/akito123321/valoStrats/Config.java new file mode 100644 index 0000000..c81cd09 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/Config.java @@ -0,0 +1,50 @@ +package me.akito123321.valoStrats; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.nimbusds.jose.util.StandardCharset; + +import me.akito123321.valoStrats.db.MySQLConfig; + +public record Config( + String host, + int port, + String clientId, + String clientSecret, + String redirect_url, + MySQLConfig sqlConfig) { + + public static Config loadConfig(String path) throws IOException { + Path configPath = Path.of(path); + if (!Files.exists(configPath)) { + Files.createFile(configPath); + } + + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + String configStr = Files.readString(configPath); + if (configStr.isEmpty()) { + Config config = new Config( + "0.0.0.0", + 8080, + "", + "", + "", + new MySQLConfig( + "", + 3306, + "root", + "root", + "")); + Files.writeString(configPath, gson.toJson(config), StandardCharset.UTF_8); + System.out.println("Config file created. Please edit it accordingly"); + System.exit(0); + } + + return gson.fromJson(configStr, Config.class); + } + } diff --git a/src/main/java/me/akito123321/valoStrats/ValoStratsApplication.java b/src/main/java/me/akito123321/valoStrats/ValoStratsApplication.java new file mode 100644 index 0000000..cb3a730 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/ValoStratsApplication.java @@ -0,0 +1,22 @@ +package me.akito123321.valoStrats; + +import java.io.IOException; +import java.util.Properties; + +import org.springframework.boot.builder.SpringApplicationBuilder; + +public class ValoStratsApplication { + public static Config config; + + public static void main(String[] args) throws IOException { + config = Config.loadConfig("config.json"); + + Properties props = new Properties(); + props.put("spring.jpa.generate-ddl", true); + + new SpringApplicationBuilder(ValoStratsApplication.class) + .properties(props) + .build() + .run(args); + } +} diff --git a/src/main/java/me/akito123321/valoStrats/db/MySQLConfig.java b/src/main/java/me/akito123321/valoStrats/db/MySQLConfig.java new file mode 100644 index 0000000..6e55d91 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/db/MySQLConfig.java @@ -0,0 +1,5 @@ +package me.akito123321.valoStrats.db; + +public record MySQLConfig(String host, int port, String username, String password, String database) { + +} diff --git a/src/main/java/me/akito123321/valoStrats/db/repositories/GroupRepository.java b/src/main/java/me/akito123321/valoStrats/db/repositories/GroupRepository.java new file mode 100644 index 0000000..5a10566 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/db/repositories/GroupRepository.java @@ -0,0 +1,11 @@ +package me.akito123321.valoStrats.db.repositories; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import me.akito123321.valoStrats.schemas.Group; + +public interface GroupRepository extends JpaRepository { + List findByOwnerUsername(String ownerId); +} diff --git a/src/main/java/me/akito123321/valoStrats/db/repositories/StratRepository.java b/src/main/java/me/akito123321/valoStrats/db/repositories/StratRepository.java new file mode 100644 index 0000000..d192893 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/db/repositories/StratRepository.java @@ -0,0 +1,8 @@ +package me.akito123321.valoStrats.db.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; + +import me.akito123321.valoStrats.schemas.Strat; + +public interface StratRepository extends JpaRepository { +} diff --git a/src/main/java/me/akito123321/valoStrats/db/repositories/UserRepository.java b/src/main/java/me/akito123321/valoStrats/db/repositories/UserRepository.java new file mode 100644 index 0000000..2b2c854 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/db/repositories/UserRepository.java @@ -0,0 +1,14 @@ +package me.akito123321.valoStrats.db.repositories; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +import me.akito123321.valoStrats.rest.util.StratsUser; + +public interface UserRepository extends JpaRepository { + Optional findByGoogleUserId(String googleUserId); + + Optional findByTokensIn(List tokens); +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/ConfigResponse.java b/src/main/java/me/akito123321/valoStrats/rest/ConfigResponse.java new file mode 100644 index 0000000..42040f6 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/ConfigResponse.java @@ -0,0 +1,5 @@ +package me.akito123321.valoStrats.rest; + +public record ConfigResponse(String clientId, String redirect_url) { + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/controller/AuthenticationController.java b/src/main/java/me/akito123321/valoStrats/rest/controller/AuthenticationController.java new file mode 100644 index 0000000..430a4ae --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/controller/AuthenticationController.java @@ -0,0 +1,43 @@ +package me.akito123321.valoStrats.rest.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +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 me.akito123321.valoStrats.rest.requests.AuthenticationRequest; +import me.akito123321.valoStrats.rest.requests.AuthenticationResponse; +import me.akito123321.valoStrats.rest.util.JDBCUserDetailsService; +import me.akito123321.valoStrats.rest.util.StratsUser; + +@RestController +@RequestMapping("/api/auth") +public class AuthenticationController { + + @Autowired + private JDBCUserDetailsService userDetailsService; + + @Autowired + private AuthenticationManager authManager; + + @PostMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity login(@RequestBody AuthenticationRequest request) { + Authentication auth = authManager.authenticate(new UsernamePasswordAuthenticationToken("", request.code())); + SecurityContextHolder.getContext().setAuthentication(auth); + + StratsUser user = (StratsUser) auth.getPrincipal(); + String token = userDetailsService.createToken(user.getUsername()); + if (token == null) + return ResponseEntity.internalServerError().build(); + + return ResponseEntity.ok(new AuthenticationResponse(user.getUsername(), user.getDisplayName(), token)); + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/controller/ConfigController.java b/src/main/java/me/akito123321/valoStrats/rest/controller/ConfigController.java new file mode 100644 index 0000000..8dc911b --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/controller/ConfigController.java @@ -0,0 +1,21 @@ +package me.akito123321.valoStrats.rest.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import me.akito123321.valoStrats.Config; +import me.akito123321.valoStrats.ValoStratsApplication; +import me.akito123321.valoStrats.rest.ConfigResponse; + +@RestController +@RequestMapping("/api/config") +public class ConfigController { + + @GetMapping + public ResponseEntity getConfig() { + Config config = ValoStratsApplication.config; + return ResponseEntity.ok(new ConfigResponse(config.clientId(), config.redirect_url())); + } +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/controller/GroupRestAPI.java b/src/main/java/me/akito123321/valoStrats/rest/controller/GroupRestAPI.java new file mode 100644 index 0000000..b58bbc6 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/controller/GroupRestAPI.java @@ -0,0 +1,227 @@ +package me.akito123321.valoStrats.rest.controller; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import me.akito123321.valoStrats.rest.requests.GroupRequest; +import me.akito123321.valoStrats.rest.requests.StratRequest; +import me.akito123321.valoStrats.rest.services.GroupService; +import me.akito123321.valoStrats.rest.services.StratService; +import me.akito123321.valoStrats.rest.util.JDBCUserDetailsService; +import me.akito123321.valoStrats.rest.util.StratsUser; +import me.akito123321.valoStrats.schemas.Group; +import me.akito123321.valoStrats.schemas.Strat; + +@RestController +@RequestMapping("/api/group") +public class GroupRestAPI { + + @Autowired + private JDBCUserDetailsService userService; + + @Autowired + private GroupService groupService; + + @Autowired + private StratService stratService; + + @PostMapping("/") + public ResponseEntity createGroup(@RequestBody GroupRequest request) { + StratsUser user = getUser(); + Group group = new Group(request.name(), user); + groupService.saveGroup(group); + return ResponseEntity.ok(group); + } + + @GetMapping("/") + public ResponseEntity> getGroups() { + StratsUser user = getUser(); + List groups = user.getGroups(); + return ResponseEntity.ok(groups); + } + + @DeleteMapping("/{id}") + public ResponseEntity removeGroup(@PathVariable String id) { + StratsUser user = getUser(); + + Group group = groupService.getGroupById(id); + if (group == null) { + return ResponseEntity.notFound().build(); + } + if (!group.getOwner().getUsername().equals(user.getUsername())) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + groupService.removeGroup(id); + + return ResponseEntity.ok(null); + } + + @PutMapping("/{id}") + public ResponseEntity updateGroup(@PathVariable String id, @RequestBody GroupRequest request) { + StratsUser user = getUser(); + + Group group = groupService.getGroupById(id); + if (group == null) { + return ResponseEntity.notFound().build(); + } + if (!group.getOwner().getUsername().equals(user.getUsername())) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + group.setName(request.name()); + + groupService.saveGroup(group); + + return ResponseEntity.ok(group); + } + + @GetMapping("/{id}") + public ResponseEntity getGroup(@PathVariable String id) { + StratsUser user = getUser(); + + Group group = groupService.getGroupById(id); + if (group == null) { + return ResponseEntity.notFound().build(); + } + if (!group.getMembers().contains(user)) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + return ResponseEntity.ok(group); + } + + @PostMapping("/{id}/member") + public ResponseEntity addUserToGroup(@PathVariable String id, @RequestBody String userId) { + StratsUser user = getUser(); + + Group group = groupService.getGroupById(id); + if (group == null) { + return ResponseEntity.notFound().build(); + } + if (!group.getOwner().getUsername().equals(user.getUsername())) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + group.getMembers().add(userService.loadUserByUsername(userId)); + + groupService.saveGroup(group); + + return ResponseEntity.ok(group); + } + + @DeleteMapping("/{id}/member/{userId}") + public ResponseEntity removeUserFromGroup(@PathVariable String id, @PathVariable String userId) { + StratsUser user = getUser(); + + Group group = groupService.getGroupById(id); + if (group == null) { + return ResponseEntity.notFound().build(); + } + if (!group.getOwner().getUsername().equals(user.getUsername()) || !user.getUsername().equals(userId)) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + group.getMembers().remove(userService.loadUserByUsername(userId)); + + groupService.saveGroup(group); + + return ResponseEntity.ok(group); + } + + @PostMapping("/{id}/strat") + public ResponseEntity addStrat(@PathVariable String id, @RequestBody StratRequest strat) { + StratsUser user = getUser(); + + Group group = groupService.getGroupById(id); + if (group == null) { + return ResponseEntity.notFound().build(); + } + if (!group.getMembers().contains(user)) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + group.getStrats().add(new Strat(strat.title(), strat.description(), group, strat.stratStates(), strat.lineups())); + + groupService.saveGroup(group); + + return ResponseEntity.ok(group); + } + + @DeleteMapping("/{id}/strat/{stratId}") + public ResponseEntity removeStrat(@PathVariable String id, @PathVariable String stratId) { + StratsUser user = getUser(); + + Group group = groupService.getGroupById(id); + if (group == null) { + return ResponseEntity.notFound().build(); + } + if (!group.getMembers().contains(user)) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + Strat strat = stratService.getStratById(stratId); + if (strat == null) { + return ResponseEntity.notFound().build(); + } + + group.getStrats().remove(strat); + + groupService.saveGroup(group); + + return ResponseEntity.ok(group); + } + + @PatchMapping("/{id}/strat/{stratId}") + public ResponseEntity editStrat(@PathVariable String id, @PathVariable String stratId, @RequestBody StratRequest newStrat) { + StratsUser user = getUser(); + + Group group = groupService.getGroupById(id); + if (group == null) { + return ResponseEntity.notFound().build(); + } + if (!group.getMembers().contains(user)) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + Strat strat = stratService.getStratById(stratId); + if (strat == null){ + return ResponseEntity.notFound().build(); + }if (!group.getStrats().contains(strat)) { + return ResponseEntity.badRequest().build(); + } + + strat.setDescription(newStrat.description()); + strat.setTitle(newStrat.title()); + strat.setStratStates(newStrat.stratStates()); + strat.setLineups(newStrat.lineups()); + + stratService.saveStrat(strat); + + groupService.saveGroup(group); + + return ResponseEntity.ok(group); + } + + + private StratsUser getUser() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + StratsUser user = (StratsUser) auth.getPrincipal(); + return user; + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/oauth2/GoogleService.java b/src/main/java/me/akito123321/valoStrats/rest/oauth2/GoogleService.java new file mode 100644 index 0000000..22ed1b2 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/oauth2/GoogleService.java @@ -0,0 +1,24 @@ +package me.akito123321.valoStrats.rest.oauth2; + +import com.google.gson.JsonObject; + +import retrofit2.Call; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.Header; +import retrofit2.http.POST; + +public interface GoogleService { + + @POST("https://oauth2.googleapis.com/token") + @FormUrlEncoded + public Call getAuthorizationToken( + @Field("client_id") String client_id, + @Field("client_secret") String client_secret, + @Field("code") String code, + @Field("grant_type") String grant_type, + @Field("redirect_uri") String redirect_uri); + + @POST("https://www.googleapis.com/oauth2/v3/userinfo") + public Call getUserInfo(@Header("Authorization") String token); +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/requests/AuthenticationRequest.java b/src/main/java/me/akito123321/valoStrats/rest/requests/AuthenticationRequest.java new file mode 100644 index 0000000..0ff6cc3 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/requests/AuthenticationRequest.java @@ -0,0 +1,5 @@ +package me.akito123321.valoStrats.rest.requests; + +public record AuthenticationRequest(String code) { + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/requests/AuthenticationResponse.java b/src/main/java/me/akito123321/valoStrats/rest/requests/AuthenticationResponse.java new file mode 100644 index 0000000..5953c91 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/requests/AuthenticationResponse.java @@ -0,0 +1,5 @@ +package me.akito123321.valoStrats.rest.requests; + +public record AuthenticationResponse(String username, String displayName, String token) { + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/requests/GroupRequest.java b/src/main/java/me/akito123321/valoStrats/rest/requests/GroupRequest.java new file mode 100644 index 0000000..6c117ae --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/requests/GroupRequest.java @@ -0,0 +1,10 @@ +package me.akito123321.valoStrats.rest.requests; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record GroupRequest( + @Size(max = 50, message = "{title longer than 50 chars}") + @NotBlank String name) { +} + diff --git a/src/main/java/me/akito123321/valoStrats/rest/requests/StratRequest.java b/src/main/java/me/akito123321/valoStrats/rest/requests/StratRequest.java new file mode 100644 index 0000000..51f2b89 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/requests/StratRequest.java @@ -0,0 +1,11 @@ +package me.akito123321.valoStrats.rest.requests; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record StratRequest( + @Size(max = 50, message = "{title longer than 50 chars}") @NotBlank String title, + @Size(max = 512, message = "{title longer than 50 chars}") @NotBlank String description, + @NotBlank byte[][] stratStates, + byte[][] lineups) { +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/services/GroupService.java b/src/main/java/me/akito123321/valoStrats/rest/services/GroupService.java new file mode 100644 index 0000000..1a535c0 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/services/GroupService.java @@ -0,0 +1,33 @@ +package me.akito123321.valoStrats.rest.services; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import me.akito123321.valoStrats.db.repositories.GroupRepository; +import me.akito123321.valoStrats.schemas.Group; + +@Service +public class GroupService { + + @Autowired + private GroupRepository groupRepository; + + public void saveGroup(Group group) { + groupRepository.save(group); + } + + public List getGroups() { + return groupRepository.findAll(); + } + + public Group getGroupById(String id) { + return groupRepository.findById(id).orElse(null); + } + + public void removeGroup(String id) { + groupRepository.deleteById(id); + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/services/StratService.java b/src/main/java/me/akito123321/valoStrats/rest/services/StratService.java new file mode 100644 index 0000000..778fdfa --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/services/StratService.java @@ -0,0 +1,23 @@ +package me.akito123321.valoStrats.rest.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import me.akito123321.valoStrats.db.repositories.StratRepository; +import me.akito123321.valoStrats.schemas.Strat; + +@Service +public class StratService { + + @Autowired + private StratRepository stratRepository; + + public Strat getStratById(String id) { + return stratRepository.findById(id).orElse(null); + } + + public void saveStrat(Strat strat) { + stratRepository.save(strat); + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/util/AuthTokenFilter.java b/src/main/java/me/akito123321/valoStrats/rest/util/AuthTokenFilter.java new file mode 100644 index 0000000..bb19a10 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/util/AuthTokenFilter.java @@ -0,0 +1,37 @@ +package me.akito123321.valoStrats.rest.util; + +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class AuthTokenFilter extends OncePerRequestFilter { + + @Autowired + private JDBCUserDetailsService userDetailsService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String authenticationHeader = request.getHeader("Authorization"); + if (authenticationHeader != null && authenticationHeader.startsWith("Bearer ")) { + String token = authenticationHeader.substring("Bearer ".length()); + StratsUser user = userDetailsService.loadUserByToken(token); + if (user != null) { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + + filterChain.doFilter(request, response); + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/util/GoogleAuthenticationManager.java b/src/main/java/me/akito123321/valoStrats/rest/util/GoogleAuthenticationManager.java new file mode 100644 index 0000000..ff68be5 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/util/GoogleAuthenticationManager.java @@ -0,0 +1,70 @@ +package me.akito123321.valoStrats.rest.util; + +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; + +import com.google.gson.JsonObject; + +import me.akito123321.valoStrats.Config; +import me.akito123321.valoStrats.ValoStratsApplication; +import me.akito123321.valoStrats.rest.oauth2.GoogleService; +import okhttp3.OkHttpClient; +import retrofit2.Call; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class GoogleAuthenticationManager extends AbstractUserDetailsAuthenticationProvider { + + @Autowired + private JDBCUserDetailsService userDetailsService; + + private OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); + private Retrofit retrofit = new Retrofit.Builder() + .baseUrl("https://cringe-studios.com") + .addConverterFactory(GsonConverterFactory.create()) + .client(httpClient.build()) + .build(); + private GoogleService service = retrofit.create(GoogleService.class); + + @Override + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + } + + @Override + protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + Config config = ValoStratsApplication.config; + String accessToken = (String) authentication.getCredentials(); + Call tokenRequest = service.getAuthorizationToken(config.clientId(), config.clientSecret(), accessToken, "authorization_code", config.redirect_url()); + + UserDetails details = null; + try { + JsonObject response = tokenRequest.execute().body(); + System.out.println(response.toString()); + + JsonObject userInfo = service.getUserInfo("Bearer " + response.get("access_token").getAsString()).execute().body(); + System.out.println(userInfo.toString()); + + String googleUserId = userInfo.get("sub").getAsString(); + String name = userInfo.get("name").getAsString(); + + details = userDetailsService.loadByGoogleId(googleUserId); + if (details == null) { + details = userDetailsService.createUser(UUID.randomUUID().toString(), "", googleUserId, name); + } + } catch (Exception e) { + e.printStackTrace(); + throw new InsufficientAuthenticationException("Failed to authenticate"); + } + + System.out.println(details); + + return details; + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/util/JDBCUserDetailsService.java b/src/main/java/me/akito123321/valoStrats/rest/util/JDBCUserDetailsService.java new file mode 100644 index 0000000..8c865e6 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/util/JDBCUserDetailsService.java @@ -0,0 +1,62 @@ +package me.akito123321.valoStrats.rest.util; + +import java.util.List; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; + +import me.akito123321.valoStrats.db.repositories.UserRepository; + +public class JDBCUserDetailsService implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + private PasswordEncoder passwordEncoder; + + public JDBCUserDetailsService(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + public StratsUser createUser(String username, String password, String googleUserId, String displayName) { + StratsUser user = new StratsUser(googleUserId, displayName, List.of(new SimpleGrantedAuthority("ROLE_USER")), username, passwordEncoder.encode(password), true, true, true, true); + userRepository.save(user); + return user; + } + + public String createToken(String username) { + StratsUser user = userRepository.findById(username).orElse(null); + if (user == null) + return null; + + String token = UUID.randomUUID().toString(); + user.getTokens().add(token); + + userRepository.save(user); + + return token; + } + + @Override + public StratsUser loadUserByUsername(String username) throws UsernameNotFoundException { + StratsUser user = userRepository.findById(username).orElse(null); + if (user == null) + throw new UsernameNotFoundException("User not found"); + return user; + } + + public UserDetails loadByGoogleId(String googleId) { + UserDetails user = userRepository.findByGoogleUserId(googleId).orElse(null); + return user; + } + + public StratsUser loadUserByToken(String token) { + return userRepository.findByTokensIn(List.of(token)).orElse(null); + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/util/StratsUser.java b/src/main/java/me/akito123321/valoStrats/rest/util/StratsUser.java new file mode 100644 index 0000000..4b5ac51 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/util/StratsUser.java @@ -0,0 +1,115 @@ +package me.akito123321.valoStrats.rest.util; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import me.akito123321.valoStrats.schemas.Group; + +@Entity(name = "user") +public class StratsUser implements UserDetails { + + private static final long serialVersionUID = 6175503410592822108L; + + @Id + private String username; + private String password; + @ElementCollection(fetch = FetchType.EAGER) + private List authorities; + private boolean isAccountNonExpired; + private boolean isAccountNonLocked; + private boolean isCredentialsNonExpired; + private boolean isEnabled; + + @ElementCollection(fetch = FetchType.EAGER) + private Set tokens; + private String googleUserId; + private String displayName; + + @ManyToMany(mappedBy = "members") + @ElementCollection(fetch = FetchType.EAGER) + @JsonIgnore + private List groups; + + public StratsUser() { + + } + + public StratsUser(String googleUserId, String displayName, List authorities, String username, String password, boolean isAccountNonExpired, boolean isAccountNonLocked, boolean isCredentialsNonExpired, boolean isEnabled) { + super(); + this.googleUserId = googleUserId; + this.displayName = displayName; + this.authorities = authorities; + this.username = username; + this.password = password; + this.isAccountNonExpired = isAccountNonExpired; + this.isAccountNonLocked = isAccountNonLocked; + this.isCredentialsNonExpired = isCredentialsNonExpired; + this.isEnabled = isEnabled; + this.tokens = new HashSet<>(); + } + + public String getGoogleId() { + return googleUserId; + } + + public String getDisplayName() { + return displayName; + } + + public Set getTokens() { + return tokens; + } + + @Override + public Collection getAuthorities() { + return authorities; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public boolean isAccountNonExpired() { + return isAccountNonExpired; + } + + @Override + public boolean isAccountNonLocked() { + return isAccountNonLocked; + } + + @Override + public boolean isCredentialsNonExpired() { + return isCredentialsNonExpired; + } + + @Override + public boolean isEnabled() { + return isEnabled; + } + + public List getGroups() { + return groups; + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/rest/util/TokenAuthenticationManager.java b/src/main/java/me/akito123321/valoStrats/rest/util/TokenAuthenticationManager.java new file mode 100644 index 0000000..27557cd --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/rest/util/TokenAuthenticationManager.java @@ -0,0 +1,27 @@ +package me.akito123321.valoStrats.rest.util; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; + +public class TokenAuthenticationManager extends AbstractUserDetailsAuthenticationProvider { + + @Autowired + private JDBCUserDetailsService userDetailsService; + + @Override + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + } + + @Override + protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + UserDetails details = userDetailsService.loadUserByToken((String) authentication.getCredentials()); + if (details == null) + throw new BadCredentialsException("Invalid token"); + return details; + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/schemas/Group.java b/src/main/java/me/akito123321/valoStrats/schemas/Group.java new file mode 100644 index 0000000..035f211 --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/schemas/Group.java @@ -0,0 +1,62 @@ +package me.akito123321.valoStrats.schemas; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import me.akito123321.valoStrats.rest.util.StratsUser; + +@Entity(name = "wishlist_group") +public class Group { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private String id; + private String name; + @ManyToOne + private StratsUser owner; + @ManyToMany + private List members; + @ManyToMany + private List strats; + + public Group(String name, StratsUser owner) { + this.name = name; + this.owner = owner; + this.members = new ArrayList(); + this.strats = new ArrayList(); + } + + public Group() { + + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public StratsUser getOwner() { + return owner; + } + + public List getMembers() { + return members; + } + + public List getStrats() { + return strats; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/main/java/me/akito123321/valoStrats/schemas/Strat.java b/src/main/java/me/akito123321/valoStrats/schemas/Strat.java new file mode 100644 index 0000000..dd2981b --- /dev/null +++ b/src/main/java/me/akito123321/valoStrats/schemas/Strat.java @@ -0,0 +1,80 @@ +package me.akito123321.valoStrats.schemas; + +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.ManyToOne; +import jakarta.persistence.OneToMany; + +@Entity +public class Strat { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private String id; + private String title; + private String description; + @ManyToOne + private Group group; + + @Lob + @Column(name = "imagedata", length = 1000) + @OneToMany + private byte[][] stratStates; + + @Lob + @Column(name = "imagedata", length = 1000) + @OneToMany + private byte[][] lineups; + + public Strat(String title, String description, Group group, byte[][] stratStates, byte[][] lineups) { + this.title = title; + this.description = description; + this.group = group; + this.stratStates = stratStates; + this.lineups = lineups; + } + + public Strat() { + + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setTitle(String title) { + this.title = title; + } + + public byte[][] getStratStates() { + return stratStates; + } + + public void setStratStates(byte[][] stratStates) { + this.stratStates = stratStates; + } + + public byte[][] getLineups() { + return lineups; + } + + public void setLineups(byte[][] lineups) { + this.lineups = lineups; + } + +} diff --git a/target/classes/META-INF/MANIFEST.MF b/target/classes/META-INF/MANIFEST.MF new file mode 100644 index 0000000..940ba88 --- /dev/null +++ b/target/classes/META-INF/MANIFEST.MF @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Build-Jdk-Spec: 21 +Implementation-Title: valoStratsBackend +Implementation-Version: 0.0.1-SNAPSHOT +Created-By: Maven Integration for Eclipse + diff --git a/target/classes/META-INF/maven/me.akito123321.valoStratsBackend/valoStratsBackend/pom.properties b/target/classes/META-INF/maven/me.akito123321.valoStratsBackend/valoStratsBackend/pom.properties new file mode 100644 index 0000000..a1e619f --- /dev/null +++ b/target/classes/META-INF/maven/me.akito123321.valoStratsBackend/valoStratsBackend/pom.properties @@ -0,0 +1,7 @@ +#Generated by Maven Integration for Eclipse +#Tue Jun 04 23:56:43 CEST 2024 +artifactId=valoStratsBackend +groupId=me.akito123321.valoStratsBackend +m2e.projectLocation=C\:\\Users\\ronny\\eclipse-workspace\\valoStratsBackend +m2e.projectName=valoStratsBackend +version=0.0.1-SNAPSHOT diff --git a/target/classes/META-INF/maven/me.akito123321.valoStratsBackend/valoStratsBackend/pom.xml b/target/classes/META-INF/maven/me.akito123321.valoStratsBackend/valoStratsBackend/pom.xml new file mode 100644 index 0000000..6cf3334 --- /dev/null +++ b/target/classes/META-INF/maven/me.akito123321.valoStratsBackend/valoStratsBackend/pom.xml @@ -0,0 +1,90 @@ + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.4 + + + me.akito123321.valoStratsBackend + valoStratsBackend + 0.0.1-SNAPSHOT + + 17 + + + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + + com.squareup.retrofit2 + retrofit + 2.10.0 + + + + com.google.code.gson + gson + + + + + com.squareup.retrofit2 + converter-gson + 2.10.0 + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.mariadb.jdbc + mariadb-java-client + + + com.sigpwned + opengraph4j + 0.0.0 + + + + org.jsoup + jsoup + 1.17.2 + + + \ No newline at end of file diff --git a/target/classes/me/akito123321/valoStrats/Config.class b/target/classes/me/akito123321/valoStrats/Config.class new file mode 100644 index 0000000..b4f8d58 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/Config.class differ diff --git a/target/classes/me/akito123321/valoStrats/ValoStratsApplication.class b/target/classes/me/akito123321/valoStrats/ValoStratsApplication.class new file mode 100644 index 0000000..b261aee Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/ValoStratsApplication.class differ diff --git a/target/classes/me/akito123321/valoStrats/db/MySQLConfig.class b/target/classes/me/akito123321/valoStrats/db/MySQLConfig.class new file mode 100644 index 0000000..79dea6f Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/db/MySQLConfig.class differ diff --git a/target/classes/me/akito123321/valoStrats/db/repositories/GroupRepository.class b/target/classes/me/akito123321/valoStrats/db/repositories/GroupRepository.class new file mode 100644 index 0000000..9d31867 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/db/repositories/GroupRepository.class differ diff --git a/target/classes/me/akito123321/valoStrats/db/repositories/StratRepository.class b/target/classes/me/akito123321/valoStrats/db/repositories/StratRepository.class new file mode 100644 index 0000000..13fab90 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/db/repositories/StratRepository.class differ diff --git a/target/classes/me/akito123321/valoStrats/db/repositories/UserRepository.class b/target/classes/me/akito123321/valoStrats/db/repositories/UserRepository.class new file mode 100644 index 0000000..f9ec58d Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/db/repositories/UserRepository.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/ConfigResponse.class b/target/classes/me/akito123321/valoStrats/rest/ConfigResponse.class new file mode 100644 index 0000000..0726411 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/ConfigResponse.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/controller/AuthenticationController.class b/target/classes/me/akito123321/valoStrats/rest/controller/AuthenticationController.class new file mode 100644 index 0000000..3435c96 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/controller/AuthenticationController.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/controller/ConfigController.class b/target/classes/me/akito123321/valoStrats/rest/controller/ConfigController.class new file mode 100644 index 0000000..f0e0b96 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/controller/ConfigController.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/controller/GroupRestAPI.class b/target/classes/me/akito123321/valoStrats/rest/controller/GroupRestAPI.class new file mode 100644 index 0000000..9e2a65d Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/controller/GroupRestAPI.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/oauth2/GoogleService.class b/target/classes/me/akito123321/valoStrats/rest/oauth2/GoogleService.class new file mode 100644 index 0000000..29d0640 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/oauth2/GoogleService.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/requests/AuthenticationRequest.class b/target/classes/me/akito123321/valoStrats/rest/requests/AuthenticationRequest.class new file mode 100644 index 0000000..dc6df7e Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/requests/AuthenticationRequest.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/requests/AuthenticationResponse.class b/target/classes/me/akito123321/valoStrats/rest/requests/AuthenticationResponse.class new file mode 100644 index 0000000..33f65e3 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/requests/AuthenticationResponse.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/requests/GroupRequest.class b/target/classes/me/akito123321/valoStrats/rest/requests/GroupRequest.class new file mode 100644 index 0000000..f97a01f Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/requests/GroupRequest.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/requests/StratRequest.class b/target/classes/me/akito123321/valoStrats/rest/requests/StratRequest.class new file mode 100644 index 0000000..bfae07e Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/requests/StratRequest.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/services/GroupService.class b/target/classes/me/akito123321/valoStrats/rest/services/GroupService.class new file mode 100644 index 0000000..79eb299 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/services/GroupService.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/services/StratService.class b/target/classes/me/akito123321/valoStrats/rest/services/StratService.class new file mode 100644 index 0000000..4052633 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/services/StratService.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/util/AuthTokenFilter.class b/target/classes/me/akito123321/valoStrats/rest/util/AuthTokenFilter.class new file mode 100644 index 0000000..5c41874 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/util/AuthTokenFilter.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/util/GoogleAuthenticationManager.class b/target/classes/me/akito123321/valoStrats/rest/util/GoogleAuthenticationManager.class new file mode 100644 index 0000000..1b994dd Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/util/GoogleAuthenticationManager.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/util/JDBCUserDetailsService.class b/target/classes/me/akito123321/valoStrats/rest/util/JDBCUserDetailsService.class new file mode 100644 index 0000000..c2f2f77 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/util/JDBCUserDetailsService.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/util/StratsUser.class b/target/classes/me/akito123321/valoStrats/rest/util/StratsUser.class new file mode 100644 index 0000000..ebb5fa1 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/util/StratsUser.class differ diff --git a/target/classes/me/akito123321/valoStrats/rest/util/TokenAuthenticationManager.class b/target/classes/me/akito123321/valoStrats/rest/util/TokenAuthenticationManager.class new file mode 100644 index 0000000..0fcccd9 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/rest/util/TokenAuthenticationManager.class differ diff --git a/target/classes/me/akito123321/valoStrats/schemas/Group.class b/target/classes/me/akito123321/valoStrats/schemas/Group.class new file mode 100644 index 0000000..75fd3d4 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/schemas/Group.class differ diff --git a/target/classes/me/akito123321/valoStrats/schemas/Strat.class b/target/classes/me/akito123321/valoStrats/schemas/Strat.class new file mode 100644 index 0000000..12f06f0 Binary files /dev/null and b/target/classes/me/akito123321/valoStrats/schemas/Strat.class differ