diff --git a/.classpath b/.classpath
index f7e4a1d..ad649b4 100644
--- a/.classpath
+++ b/.classpath
@@ -14,21 +14,16 @@
+
-
-
-
-
-
-
-
+
@@ -36,5 +31,10 @@
+
+
+
+
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 2f5cc74..afa8c73 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -1,8 +1,13 @@
eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
-org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=25
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=25
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
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=disabled
-org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=25
diff --git a/core/.classpath b/core/.classpath
index f7e4a1d..7d5324c 100644
--- a/core/.classpath
+++ b/core/.classpath
@@ -26,15 +26,28 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/.project b/core/.project
index 1a64507..529a1c6 100644
--- a/core/.project
+++ b/core/.project
@@ -20,4 +20,15 @@
org.eclipse.jdt.core.javanature
org.eclipse.m2e.core.maven2Nature
+
+
+ 1768579733030
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
diff --git a/core/.settings/org.eclipse.jdt.apt.core.prefs b/core/.settings/org.eclipse.jdt.apt.core.prefs
new file mode 100644
index 0000000..dfa4f3a
--- /dev/null
+++ b/core/.settings/org.eclipse.jdt.apt.core.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.apt.aptEnabled=true
+org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations
+org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations
diff --git a/core/.settings/org.eclipse.jdt.core.prefs b/core/.settings/org.eclipse.jdt.core.prefs
index 2f5cc74..85a7c3d 100644
--- a/core/.settings/org.eclipse.jdt.core.prefs
+++ b/core/.settings/org.eclipse.jdt.core.prefs
@@ -1,8 +1,15 @@
eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
-org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=25
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=25
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
-org.eclipse.jdt.core.compiler.release=disabled
-org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.processAnnotations=enabled
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=25
diff --git a/core/src/main/java/net/miarma/backend/core/config/JwtFilter.java b/core/src/main/java/net/miarma/backend/core/config/JwtFilter.java
index 3bd250d..80efc68 100644
--- a/core/src/main/java/net/miarma/backend/core/config/JwtFilter.java
+++ b/core/src/main/java/net/miarma/backend/core/config/JwtFilter.java
@@ -8,7 +8,6 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -36,6 +35,8 @@ public class JwtFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
+
+ System.out.println("JwtFilter ejecutándose para " + request.getRequestURI());
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
@@ -44,19 +45,27 @@ public class JwtFilter extends OncePerRequestFilter {
try {
if (jwtService.validateToken(token)) {
UUID userId = jwtService.getUserId(token);
- short serviceId = jwtService.getServiceId(token);
+ Byte serviceId = jwtService.getServiceId(token);
User user = userService.getById(userId);
+ String roleName = switch(user.getGlobalRole()) {
+ case 0 -> "USER";
+ case 1 -> "ADMIN";
+ default -> "GUEST";
+ };
+
List authorities = List.of(
- new SimpleGrantedAuthority("ROLE_" + user.getGlobalRole())
+ new SimpleGrantedAuthority("ROLE_" + roleName)
);
-
+
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, authorities);
- auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
+ System.out.println("Granted Authorities: " +
+ SecurityContextHolder.getContext().getAuthentication().getAuthorities());
+
long timeLeft = jwtService.getExpiration(token).getTime() - System.currentTimeMillis();
if (timeLeft < refreshThreshold) {
String newToken = jwtService.generateToken(userId, serviceId);
@@ -68,7 +77,7 @@ public class JwtFilter extends OncePerRequestFilter {
return;
}
}
-
+
filterChain.doFilter(request, response);
}
}
\ No newline at end of file
diff --git a/core/src/main/java/net/miarma/backend/core/config/SecurityConfig.java b/core/src/main/java/net/miarma/backend/core/config/SecurityConfig.java
index da00bb9..55ea65c 100644
--- a/core/src/main/java/net/miarma/backend/core/config/SecurityConfig.java
+++ b/core/src/main/java/net/miarma/backend/core/config/SecurityConfig.java
@@ -2,6 +2,9 @@ package net.miarma.backend.core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
@@ -12,6 +15,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
@Configuration
@EnableWebSecurity
+@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private final JwtFilter jwtFilter;
@@ -33,6 +37,12 @@ public class SecurityConfig {
return http.build();
}
+
+ @Bean
+ public AuthenticationManager authManager(HttpSecurity http) throws Exception {
+ return http.getSharedObject(AuthenticationManagerBuilder.class)
+ .build();
+ }
@Bean
public PasswordEncoder passwordEncoder() {
diff --git a/core/src/main/java/net/miarma/backend/core/controller/AuthController.java b/core/src/main/java/net/miarma/backend/core/controller/AuthController.java
index 11aa3a1..63bd525 100644
--- a/core/src/main/java/net/miarma/backend/core/controller/AuthController.java
+++ b/core/src/main/java/net/miarma/backend/core/controller/AuthController.java
@@ -4,6 +4,7 @@ import java.util.Map;
import java.util.UUID;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
@@ -11,21 +12,30 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.validation.Valid;
+import net.miarma.backend.core.dto.ChangePasswordRequest;
import net.miarma.backend.core.dto.LoginRequest;
import net.miarma.backend.core.dto.LoginResponse;
+import net.miarma.backend.core.dto.RegisterRequest;
+import net.miarma.backend.core.model.Credential;
import net.miarma.backend.core.service.AuthService;
+import net.miarma.backend.core.service.CredentialService;
import net.miarma.backend.core.service.JwtService;
@RestController
@RequestMapping("/auth")
public class AuthController {
- private final AuthService authService;
+ private final CredentialService credentialService;
private final JwtService jwtService;
+ private final PasswordEncoder passwordEncoder;
+ private final AuthService authService;
- public AuthController(AuthService authService, JwtService jwtService) {
- this.authService = authService;
+ public AuthController(CredentialService credentialService, JwtService jwtService,
+ PasswordEncoder passwordEncoder, AuthService authService) {
+ this.credentialService = credentialService;
this.jwtService = jwtService;
+ this.passwordEncoder = passwordEncoder;
+ this.authService = authService;
}
@PostMapping("/login")
@@ -34,6 +44,11 @@ public class AuthController {
return ResponseEntity.ok(response);
}
+ @PostMapping("/register")
+ public ResponseEntity register(@RequestBody RegisterRequest request) {
+ return ResponseEntity.ok(authService.register(request));
+ }
+
@PostMapping("/refresh")
public ResponseEntity> refreshToken(@RequestHeader("Authorization") String authHeader) {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
@@ -46,7 +61,7 @@ public class AuthController {
}
UUID userId = jwtService.getUserId(token);
- short serviceId = jwtService.getServiceId(token);
+ Byte serviceId = jwtService.getServiceId(token);
String newToken = jwtService.generateToken(userId, serviceId);
@@ -56,5 +71,37 @@ public class AuthController {
"serviceId", serviceId
));
}
+
+ @PostMapping("/change-password")
+ public ResponseEntity> changePassword(
+ @RequestHeader("Authorization") String authHeader,
+ @Valid @RequestBody ChangePasswordRequest request
+ ) {
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+ return ResponseEntity.status(401).body("Token missing");
+ }
+ String token = authHeader.substring(7);
+ if (!jwtService.validateToken(token)) {
+ return ResponseEntity.status(401).body("Invalid token");
+ }
+
+ UUID userId = jwtService.getUserId(token);
+
+ Credential cred = credentialService.getByUserId(userId)
+ .stream()
+ .filter(c -> c.getServiceId().equals(request.getServiceId()))
+ .findFirst().get();
+ if (cred == null) {
+ return ResponseEntity.status(404).body("Credential not found");
+ }
+
+ if (!passwordEncoder.matches(request.getOldPassword(), cred.getPassword())) {
+ return ResponseEntity.status(400).body("Old password is incorrect");
+ }
+
+ credentialService.updatePassword(cred.getCredentialId(), request);
+
+ return ResponseEntity.ok(Map.of("message", "Password changed successfully"));
+ }
}
diff --git a/core/src/main/java/net/miarma/backend/core/controller/CredentialController.java b/core/src/main/java/net/miarma/backend/core/controller/CredentialController.java
index 96e56ee..5885bfd 100644
--- a/core/src/main/java/net/miarma/backend/core/controller/CredentialController.java
+++ b/core/src/main/java/net/miarma/backend/core/controller/CredentialController.java
@@ -1,5 +1,64 @@
package net.miarma.backend.core.controller;
+import java.util.List;
+import java.util.UUID;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+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 net.miarma.backend.core.dto.CredentialDto;
+import net.miarma.backend.core.model.Credential;
+import net.miarma.backend.core.service.CredentialService;
+
+@RestController
+@RequestMapping("/credentials")
public class CredentialController {
+ private final CredentialService credentialService;
+
+ public CredentialController(CredentialService credentialService) {
+ this.credentialService = credentialService;
+ }
+
+ @GetMapping
+ @PreAuthorize("hasRole('ADMIN')")
+ public ResponseEntity> getAll() {
+ return ResponseEntity.ok(credentialService.getAll());
+ }
+
+ @GetMapping("/user/{userId}")
+ @PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.userId")
+ public ResponseEntity> getByUserId(@PathVariable("userId") UUID userId) {
+ return ResponseEntity.ok(credentialService.getByUserId(userId));
+ }
+
+ @GetMapping("/{credentialId}")
+ @PreAuthorize("hasRole('ADMIN') or @credentialService.isOwner(#credentialId, authentication.principal.userId)")
+ public ResponseEntity getById(@PathVariable("credentialId") UUID credentialId) {
+ return ResponseEntity.ok(credentialService.getById(credentialId));
+ }
+
+ @PutMapping("/{credentialId}")
+ @PreAuthorize("hasRole('ADMIN') or @credentialService.isOwner(#credentialId, authentication.principal.userId)")
+ public ResponseEntity update(
+ @PathVariable("credentialId") UUID credentialId,
+ @RequestBody CredentialDto dto
+ ) {
+ dto.setCredentialId(credentialId);
+ return ResponseEntity.ok(credentialService.update(credentialId, dto));
+ }
+
+ @DeleteMapping("/{credentialId}")
+ @PreAuthorize("hasRole('ADMIN') or @credentialService.isOwner(#credentialId, authentication.principal.userId)")
+ public ResponseEntity delete(@PathVariable("credentialId") UUID credentialId) {
+ credentialService.delete(credentialId);
+ return ResponseEntity.noContent().build();
+ }
}
diff --git a/core/src/main/java/net/miarma/backend/core/controller/FileController.java b/core/src/main/java/net/miarma/backend/core/controller/FileController.java
index d2dea16..7f30177 100644
--- a/core/src/main/java/net/miarma/backend/core/controller/FileController.java
+++ b/core/src/main/java/net/miarma/backend/core/controller/FileController.java
@@ -1,5 +1,87 @@
package net.miarma.backend.core.controller;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+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.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import net.miarma.backend.core.model.File;
+import net.miarma.backend.core.service.FileService;
+
+@RestController
+@RequestMapping("/files")
public class FileController {
+ private final FileService fileService;
+
+ public FileController(FileService fileService) {
+ this.fileService = fileService;
+ }
+
+ @GetMapping
+ @PreAuthorize("hasRole('ADMIN')")
+ public ResponseEntity> getAll(@RequestParam Map params) {
+ List files = fileService.getAll(params);
+ return ResponseEntity.ok(files);
+ }
+
+ @GetMapping("/{fileId}")
+ @PreAuthorize("hasRole('ADMIN') or @fileService.isOwner(#fileId, authentication.principal.userId)")
+ public ResponseEntity getById(@PathVariable UUID fileId) {
+ File file = fileService.getById(fileId);
+ return ResponseEntity.ok(file);
+ }
+
+ @PostMapping
+ @PreAuthorize("hasRole('ADMIN') or #uploadedBy == authentication.principal.userId")
+ public ResponseEntity create(
+ @RequestParam String fileName,
+ @RequestParam String mimeType,
+ @RequestParam UUID uploadedBy,
+ @RequestParam Short context,
+ @RequestPart("file") MultipartFile file
+ ) throws IOException {
+ File entity = new File();
+ entity.setFileName(fileName);
+ entity.setMimeType(mimeType);
+ entity.setUploadedBy(uploadedBy);
+ entity.setContext(context);
+
+ File created = fileService.create(entity, file.getBytes());
+ return ResponseEntity.status(HttpStatus.CREATED).body(created);
+ }
+
+ @PutMapping("/{fileId}")
+ @PreAuthorize("hasRole('ADMIN') or @fileService.isOwner(#fileId, authentication.principal.userId)")
+ public ResponseEntity update(@PathVariable UUID fileId, @RequestBody File file) {
+ file.setFileId(fileId);
+ File updated = fileService.update(file);
+ return ResponseEntity.ok(updated);
+ }
+
+ @DeleteMapping("/{fileId}")
+ @PreAuthorize("hasRole('ADMIN') or @fileService.isOwner(#fileId, authentication.principal.userId)")
+ public ResponseEntity delete(@PathVariable UUID fileId, @RequestBody Map body) throws IOException {
+ String filePath = body.get("file_path");
+ Files.deleteIfExists(Paths.get(filePath));
+ fileService.delete(fileId);
+ return ResponseEntity.noContent().build();
+ }
}
diff --git a/core/src/main/java/net/miarma/backend/core/controller/UserController.java b/core/src/main/java/net/miarma/backend/core/controller/UserController.java
index aa465e9..32ee003 100644
--- a/core/src/main/java/net/miarma/backend/core/controller/UserController.java
+++ b/core/src/main/java/net/miarma/backend/core/controller/UserController.java
@@ -1,10 +1,80 @@
package net.miarma.backend.core.controller;
+import java.util.List;
+import java.util.UUID;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import net.miarma.backend.core.dto.UserDto;
+import net.miarma.backend.core.model.User;
+import net.miarma.backend.core.service.JwtService;
+import net.miarma.backend.core.service.UserService;
+
@RestController
@RequestMapping("/users")
public class UserController {
+ private UserService userService;
+ private JwtService jwtService;
+ public UserController(UserService userService, JwtService jwtService) {
+ this.userService = userService;
+ this.jwtService = jwtService;
+ }
+
+ @GetMapping
+ @PreAuthorize("hasRole('ADMIN')")
+ public ResponseEntity> getAll() {
+ return ResponseEntity.ok(
+ userService.getAll()
+ .stream()
+ .map(UserDto::fromEntity)
+ .toList()
+ );
+ }
+
+ @GetMapping("/{user_id}")
+ @PreAuthorize("hasRole('ADMIN')")
+ public ResponseEntity getById(@PathVariable("user_id") UUID userId) {
+ User user = userService.getById(userId);
+ return ResponseEntity.ok(UserDto.fromEntity(user));
+ }
+
+ @PutMapping("/{user_id}")
+ @PreAuthorize("hasRole('ADMIN') or #userId == principal.userId")
+ public ResponseEntity update(@PathVariable("user_id") UUID userId, @RequestBody UserDto dto) {
+ return ResponseEntity.ok(UserDto.fromEntity(userService.update(userId, dto)));
+ }
+
+ @GetMapping("/{user_id}/avatar")
+ public ResponseEntity getAvatar(@PathVariable("user_id") UUID userId) {
+ return ResponseEntity.ok(userService.getById(userId).getAvatar());
+ }
+
+ @GetMapping("/me")
+ public ResponseEntity getMe(@RequestHeader("Authorization") String authHeader) {
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
+ }
+
+ String token = authHeader.substring(7);
+
+ UUID userId;
+ try {
+ userId = jwtService.getUserId(token);
+ } catch (Exception e) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
+ }
+
+ User user = userService.getById(userId);
+ return ResponseEntity.ok(UserDto.fromEntity(user));
+ }
}
diff --git a/core/src/main/java/net/miarma/backend/core/dto/ChangePasswordRequest.java b/core/src/main/java/net/miarma/backend/core/dto/ChangePasswordRequest.java
new file mode 100644
index 0000000..46af3fc
--- /dev/null
+++ b/core/src/main/java/net/miarma/backend/core/dto/ChangePasswordRequest.java
@@ -0,0 +1,45 @@
+package net.miarma.backend.core.dto;
+
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+public class ChangePasswordRequest {
+
+ @NotBlank
+ private String oldPassword;
+
+ @NotBlank
+ private String newPassword;
+
+ @NotNull
+ @Min(0)
+ private Byte serviceId;
+
+ public String getOldPassword() {
+ return oldPassword;
+ }
+
+ public void setOldPassword(String oldPassword) {
+ this.oldPassword = oldPassword;
+ }
+
+ public String getNewPassword() {
+ return newPassword;
+ }
+
+ public void setNewPassword(String newPassword) {
+ this.newPassword = newPassword;
+ }
+
+ public Byte getServiceId() {
+ return serviceId;
+ }
+
+ public void setServiceId(Byte serviceId) {
+ this.serviceId = serviceId;
+ }
+
+
+}
+
diff --git a/core/src/main/java/net/miarma/backend/core/dto/CredentialDto.java b/core/src/main/java/net/miarma/backend/core/dto/CredentialDto.java
new file mode 100644
index 0000000..b5e9cf3
--- /dev/null
+++ b/core/src/main/java/net/miarma/backend/core/dto/CredentialDto.java
@@ -0,0 +1,89 @@
+package net.miarma.backend.core.dto;
+
+import java.time.Instant;
+import java.util.UUID;
+
+import net.miarma.backend.core.model.Credential;
+
+public class CredentialDto {
+ private UUID credentialId;
+ private Byte serviceId;
+ private String username;
+ private String email;
+ private Byte status;
+ private Instant createdAt;
+ private Instant updatedAt;
+
+ public CredentialDto() {}
+
+ public CredentialDto(Credential credential) {
+ this.credentialId = credential.getCredentialId();
+ this.serviceId = credential.getServiceId();
+ this.username = credential.getUsername();
+ this.email = credential.getEmail();
+ this.status = credential.getStatus();
+ this.createdAt = credential.getCreatedAt();
+ this.updatedAt = credential.getUpdatedAt();
+ }
+
+ public static CredentialDto fromEntity(Credential credential) {
+ if (credential == null) return null;
+ return new CredentialDto(credential);
+ }
+
+ public UUID getCredentialId() {
+ return credentialId;
+ }
+
+ public void setCredentialId(UUID credentialId) {
+ this.credentialId = credentialId;
+ }
+
+ public Byte getServiceId() {
+ return serviceId;
+ }
+
+ public void setServiceId(Byte serviceId) {
+ this.serviceId = serviceId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public Byte getStatus() {
+ return status;
+ }
+
+ public void setStatus(Byte status) {
+ this.status = status;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Instant getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Instant updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+}
diff --git a/core/src/main/java/net/miarma/backend/core/dto/LoginRequest.java b/core/src/main/java/net/miarma/backend/core/dto/LoginRequest.java
index bb7b436..888949f 100644
--- a/core/src/main/java/net/miarma/backend/core/dto/LoginRequest.java
+++ b/core/src/main/java/net/miarma/backend/core/dto/LoginRequest.java
@@ -9,7 +9,7 @@ public class LoginRequest {
@NotBlank
private String password;
- private short serviceId;
+ private Byte serviceId;
public String getUsername() {
return username;
@@ -27,11 +27,11 @@ public class LoginRequest {
this.password = password;
}
- public short getServiceId() {
+ public Byte getServiceId() {
return serviceId;
}
- public void setServiceId(short serviceId) {
+ public void setServiceId(Byte serviceId) {
this.serviceId = serviceId;
}
}
diff --git a/core/src/main/java/net/miarma/backend/core/dto/LoginResponse.java b/core/src/main/java/net/miarma/backend/core/dto/LoginResponse.java
index 194732a..3a789a8 100644
--- a/core/src/main/java/net/miarma/backend/core/dto/LoginResponse.java
+++ b/core/src/main/java/net/miarma/backend/core/dto/LoginResponse.java
@@ -2,13 +2,13 @@ package net.miarma.backend.core.dto;
public class LoginResponse {
private String token;
- private short serviceId;
private UserDto user;
+ private CredentialDto account;
- public LoginResponse(String token, short serviceId, UserDto user) {
+ public LoginResponse(String token, UserDto user, CredentialDto account) {
this.token = token;
- this.serviceId = serviceId;
this.user = user;
+ this.account = account;
}
public String getToken() {
@@ -19,14 +19,6 @@ public class LoginResponse {
this.token = token;
}
- public short getServiceId() {
- return serviceId;
- }
-
- public void setServiceId(short serviceId) {
- this.serviceId = serviceId;
- }
-
public UserDto getUser() {
return user;
}
@@ -34,4 +26,12 @@ public class LoginResponse {
public void setUser(UserDto user) {
this.user = user;
}
+
+ public CredentialDto getAccount() {
+ return account;
+ }
+
+ public void setAccount(CredentialDto account) {
+ this.account = account;
+ }
}
\ No newline at end of file
diff --git a/core/src/main/java/net/miarma/backend/core/dto/RegisterRequest.java b/core/src/main/java/net/miarma/backend/core/dto/RegisterRequest.java
new file mode 100644
index 0000000..f6f8bea
--- /dev/null
+++ b/core/src/main/java/net/miarma/backend/core/dto/RegisterRequest.java
@@ -0,0 +1,41 @@
+package net.miarma.backend.core.dto;
+
+public class RegisterRequest {
+ private String username;
+ private String email;
+ private String password;
+ private String displayName;
+ private Byte serviceId;
+ public String getUsername() {
+ return username;
+ }
+ public void setUsername(String username) {
+ this.username = username;
+ }
+ public String getEmail() {
+ return email;
+ }
+ public void setEmail(String email) {
+ this.email = email;
+ }
+ public String getPassword() {
+ return password;
+ }
+ public void setPassword(String password) {
+ this.password = password;
+ }
+ public String getDisplayName() {
+ return displayName;
+ }
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+ public Byte getServiceId() {
+ return serviceId;
+ }
+ public void setServiceId(Byte serviceId) {
+ this.serviceId = serviceId;
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/net/miarma/backend/core/dto/UserDto.java b/core/src/main/java/net/miarma/backend/core/dto/UserDto.java
index 58dd2a2..985ce38 100644
--- a/core/src/main/java/net/miarma/backend/core/dto/UserDto.java
+++ b/core/src/main/java/net/miarma/backend/core/dto/UserDto.java
@@ -3,6 +3,8 @@ package net.miarma.backend.core.dto;
import java.time.Instant;
import java.util.UUID;
+import net.miarma.backend.core.model.User;
+
public class UserDto {
private UUID userId;
private String displayName;
@@ -12,6 +14,8 @@ public class UserDto {
private Instant createdAt;
private Instant updatedAt;
+ public UserDto() {}
+
public UserDto(UUID userId, String displayName, String avatar, Byte globalStatus, Byte globalRole,
Instant createdAt, Instant updatedAt) {
this.userId = userId;
@@ -23,6 +27,20 @@ public class UserDto {
this.updatedAt = updatedAt;
}
+ public static UserDto fromEntity(User user) {
+ if (user == null) return null;
+
+ return new UserDto(
+ user.getUserId(),
+ user.getDisplayName(),
+ user.getAvatar(),
+ user.getGlobalStatus(),
+ user.getGlobalRole(),
+ user.getCreatedAt(),
+ user.getUpdatedAt()
+ );
+ }
+
public UUID getUserId() {
return userId;
}
diff --git a/core/src/main/java/net/miarma/backend/core/model/Credential.java b/core/src/main/java/net/miarma/backend/core/model/Credential.java
index 790b109..2a83539 100644
--- a/core/src/main/java/net/miarma/backend/core/model/Credential.java
+++ b/core/src/main/java/net/miarma/backend/core/model/Credential.java
@@ -3,10 +3,8 @@ package net.miarma.backend.core.model;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.UUID;
-
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
-
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
@@ -21,81 +19,83 @@ import jakarta.persistence.Transient;
import jakarta.persistence.UniqueConstraint;
@Entity
-@Table(
- name = "credentials",
- uniqueConstraints = {
- @UniqueConstraint(columnNames = {"service_id", "username"}),
- @UniqueConstraint(columnNames = {"service_id", "email"})
- }
-)
+@Table(name = "credentials",
+ uniqueConstraints = {
+ @UniqueConstraint(columnNames = { "service_id", "username" }),
+ @UniqueConstraint(columnNames = { "service_id", "email" })
+ })
+
public class Credential {
-
+
@Id
- @Column(columnDefinition = "BINARY(16)")
- private byte[] credentialIdBin;
+ @Column(name = "credential_id", columnDefinition = "BINARY(16)")
+ private byte[] credentialIdBin;
+
+ @Column(name = "user_id", columnDefinition = "BINARY(16)")
+ private byte[] userIdBin;
+
+ @Transient
+ private UUID credentialId;
+
+ @Transient
+ private UUID userId;
+
+ @Column(name = "service_id")
+ private Byte serviceId;
+
+ private String username;
+ private String email;
+ private String password;
+ private Byte status;
+
+ @CreationTimestamp
+ private Instant createdAt;
+
+ @UpdateTimestamp
+ private Instant updatedAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id", insertable = false, updatable = false)
+ private User user;
- @Column(name = "user_id", columnDefinition = "BINARY(16)")
- private byte[] userIdBin;
-
- @Transient
- private UUID credentialId;
+ @PrePersist
+ @PreUpdate
+ private void prePersist() {
+ if (credentialId != null) {
+ ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
+ bb.putLong(credentialId.getMostSignificantBits());
+ bb.putLong(credentialId.getLeastSignificantBits());
+ credentialIdBin = bb.array();
+ }
+ if (userId != null) {
+ ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
+ bb.putLong(userId.getMostSignificantBits());
+ bb.putLong(userId.getLeastSignificantBits());
+ userIdBin = bb.array();
+ }
+ }
- @Transient
- private UUID userId;
-
- @Column(name = "service_id")
- private Byte serviceId;
-
- private String username;
- private String email;
- private String password;
-
- private Byte status;
-
- @CreationTimestamp
- private Instant createdAt;
-
- @UpdateTimestamp
- private Instant updatedAt;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id", insertable = false, updatable = false)
- private User user;
-
- @PrePersist
- @PreUpdate
- private void prePersist() {
- if (credentialId != null) {
- ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
- bb.putLong(credentialId.getMostSignificantBits());
- bb.putLong(credentialId.getLeastSignificantBits());
- credentialIdBin = bb.array();
- }
- if (userId != null) {
- ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
- bb.putLong(userId.getMostSignificantBits());
- bb.putLong(userId.getLeastSignificantBits());
- userIdBin = bb.array();
- }
- }
-
- @PostLoad
- private void postLoad() {
- if (credentialIdBin != null) {
- ByteBuffer bb = ByteBuffer.wrap(credentialIdBin);
- long high = bb.getLong();
- long low = bb.getLong();
- credentialId = new UUID(high, low);
- }
- if (userIdBin != null) {
- ByteBuffer bb = ByteBuffer.wrap(userIdBin);
- long high = bb.getLong();
- long low = bb.getLong();
- userId = new UUID(high, low);
- }
- }
+ @PostLoad
+ private void postLoad() {
+ if (credentialIdBin != null) {
+ ByteBuffer bb = ByteBuffer.wrap(credentialIdBin);
+ long high = bb.getLong();
+ long low = bb.getLong();
+ credentialId = new UUID(high, low);
+ }
+ if (userIdBin != null) {
+ ByteBuffer bb = ByteBuffer.wrap(userIdBin);
+ long high = bb.getLong();
+ long low = bb.getLong();
+ userId = new UUID(high, low);
+ }
+ }
public UUID getCredentialId() {
+ if (credentialId == null && credentialIdBin != null) {
+ ByteBuffer bb = ByteBuffer.wrap(credentialIdBin);
+ credentialId = new UUID(bb.getLong(), bb.getLong());
+ }
return credentialId;
}
@@ -174,4 +174,4 @@ public class Credential {
public void setUser(User user) {
this.user = user;
}
-}
+}
\ No newline at end of file
diff --git a/core/src/main/java/net/miarma/backend/core/repository/CredentialRepository.java b/core/src/main/java/net/miarma/backend/core/repository/CredentialRepository.java
index a082a50..84fb188 100644
--- a/core/src/main/java/net/miarma/backend/core/repository/CredentialRepository.java
+++ b/core/src/main/java/net/miarma/backend/core/repository/CredentialRepository.java
@@ -5,18 +5,35 @@ import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
import net.miarma.backend.core.model.Credential;
-public interface CredentialRepository extends JpaRepository {
+public interface CredentialRepository extends JpaRepository {
- Optional findByServiceIdAndUsername(short serviceId, String username);
+ @Query("""
+ SELECT c FROM Credential c
+ JOIN FETCH c.user
+ WHERE c.serviceId = :serviceId
+ AND c.username = :username
+ """)
+ Optional findByServiceIdAndUsername(@Param("serviceId") Byte serviceId,
+ @Param("username") String username);
- Optional findByServiceIdAndEmail(short serviceId, String email);
+ Optional findByServiceIdAndEmail(Byte serviceId, String email);
- Optional findByUserIdAndServiceId(UUID userId, short serviceId);
+ Optional findByUserIdAndServiceId(UUID userId, Byte serviceId);
- List findByUserId(UUID userId);
+ Optional findByUsernameAndServiceId(String username, int serviceId);
+ @Query("SELECT c FROM Credential c WHERE c.userIdBin = :userIdBin")
+ List findByUserId(@Param("userIdBin") byte[] userIdBin);
+
+ Optional findByEmail(String email);
+
+ boolean existsByUsernameAndServiceId(String username, Byte serviceId);
+
+ boolean existsByEmailAndServiceId(String email, Byte serviceId);
}
diff --git a/core/src/main/java/net/miarma/backend/core/repository/FileRepository.java b/core/src/main/java/net/miarma/backend/core/repository/FileRepository.java
index 95919ba..4a8d4f6 100644
--- a/core/src/main/java/net/miarma/backend/core/repository/FileRepository.java
+++ b/core/src/main/java/net/miarma/backend/core/repository/FileRepository.java
@@ -1,17 +1,13 @@
package net.miarma.backend.core.repository;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import net.miarma.backend.core.model.File;
-public interface FileRepository extends JpaRepository {
-
- Optional findById(UUID fileId);
-
+public interface FileRepository extends JpaRepository {
List findByUploadedBy(UUID uploadedBy);
List findByContext(short context);
diff --git a/core/src/main/java/net/miarma/backend/core/repository/UserRepository.java b/core/src/main/java/net/miarma/backend/core/repository/UserRepository.java
index 3973cd0..da95016 100644
--- a/core/src/main/java/net/miarma/backend/core/repository/UserRepository.java
+++ b/core/src/main/java/net/miarma/backend/core/repository/UserRepository.java
@@ -1,11 +1,9 @@
package net.miarma.backend.core.repository;
-import java.util.UUID;
-
import org.springframework.data.jpa.repository.JpaRepository;
import net.miarma.backend.core.model.User;
-public interface UserRepository extends JpaRepository {
+public interface UserRepository extends JpaRepository {
}
diff --git a/core/src/main/java/net/miarma/backend/core/service/AuthService.java b/core/src/main/java/net/miarma/backend/core/service/AuthService.java
index 754aa82..af0dd42 100644
--- a/core/src/main/java/net/miarma/backend/core/service/AuthService.java
+++ b/core/src/main/java/net/miarma/backend/core/service/AuthService.java
@@ -1,46 +1,76 @@
package net.miarma.backend.core.service;
+import java.util.UUID;
+
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
+import net.miarma.backend.core.dto.CredentialDto;
import net.miarma.backend.core.dto.LoginRequest;
import net.miarma.backend.core.dto.LoginResponse;
+import net.miarma.backend.core.dto.RegisterRequest;
import net.miarma.backend.core.dto.UserDto;
import net.miarma.backend.core.model.Credential;
+import net.miarma.backend.core.model.User;
@Service
public class AuthService {
private final CredentialService credentialService;
+ private final UserService userService;
private final JwtService jwtService;
private final PasswordEncoder passwordEncoder;
- public AuthService(CredentialService credentialService, JwtService jwtService,
- PasswordEncoder passwordEncoder) {
+ public AuthService(CredentialService credentialService, UserService userService,
+ JwtService jwtService, PasswordEncoder passwordEncoder) {
this.credentialService = credentialService;
+ this.userService = userService;
this.jwtService = jwtService;
this.passwordEncoder = passwordEncoder;
}
public LoginResponse login(LoginRequest request) {
Credential cred = credentialService.getByUserIdAndService(request.getServiceId(), request.getUsername());
-
+
if (!passwordEncoder.matches(request.getPassword(), cred.getPassword())) {
throw new RuntimeException("Invalid credentials");
}
String token = jwtService.generateToken(cred.getUserId(), request.getServiceId());
- UserDto userDto = new UserDto(
- cred.getUser().getUserId(),
- cred.getUser().getDisplayName(),
- cred.getUser().getAvatar(),
- cred.getUser().getGlobalStatus(),
- cred.getUser().getGlobalRole(),
- cred.getUser().getCreatedAt(),
- cred.getUser().getUpdatedAt()
- );
+ UserDto userDto = UserDto.fromEntity(cred.getUser());
+
+ CredentialDto credentialDto = CredentialDto.fromEntity(cred);
- return new LoginResponse(token, request.getServiceId(), userDto);
+ return new LoginResponse(token, userDto, credentialDto);
+ }
+
+ public LoginResponse register(RegisterRequest request) {
+ if (credentialService.existsByUsernameAndService(request.getUsername(), request.getServiceId())) {
+ throw new RuntimeException("Username already taken");
+ }
+
+ User user;
+ try {
+ user = credentialService.getByEmail(request.getEmail());
+ } catch (RuntimeException e) {
+ UserDto dto = new UserDto();
+ dto.setUserId(UUID.randomUUID());
+ dto.setDisplayName(request.getDisplayName());
+ user = userService.create(dto);
+ }
+
+ Credential cred = new Credential();
+ cred.setCredentialId(UUID.randomUUID());
+ cred.setUser(user);
+ cred.setServiceId((byte) request.getServiceId());
+ cred.setUsername(request.getUsername());
+ cred.setEmail(request.getEmail());
+ cred.setPassword(passwordEncoder.encode(request.getPassword()));
+ credentialService.create(cred);
+
+ String token = jwtService.generateToken(user.getUserId(), request.getServiceId());
+
+ return new LoginResponse(token, UserDto.fromEntity(user), CredentialDto.fromEntity(cred));
}
}
diff --git a/core/src/main/java/net/miarma/backend/core/service/CredentialService.java b/core/src/main/java/net/miarma/backend/core/service/CredentialService.java
index 7d16a68..44e6441 100644
--- a/core/src/main/java/net/miarma/backend/core/service/CredentialService.java
+++ b/core/src/main/java/net/miarma/backend/core/service/CredentialService.java
@@ -3,47 +3,143 @@ package net.miarma.backend.core.service;
import java.util.List;
import java.util.UUID;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import net.miarma.backend.core.dto.ChangePasswordRequest;
+import net.miarma.backend.core.dto.CredentialDto;
import net.miarma.backend.core.model.Credential;
+import net.miarma.backend.core.model.User;
import net.miarma.backend.core.repository.CredentialRepository;
+import net.miarma.backend.core.util.UuidUtils;
@Service
@Transactional
public class CredentialService {
private final CredentialRepository credentialRepository;
+ private final PasswordEncoder passwordEncoder;
- public CredentialService(CredentialRepository credentialRepository) {
+ public CredentialService(CredentialRepository credentialRepository, PasswordEncoder passwordEncoder) {
this.credentialRepository = credentialRepository;
+ this.passwordEncoder = passwordEncoder;
}
- public Credential getById(UUID id) {
- return credentialRepository.findById(id)
+ public Credential getById(UUID credentialId) {
+ byte[] idBytes = UuidUtils.uuidToBin(credentialId);
+ return credentialRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Credential not found"));
}
public Credential create(Credential credential) {
- // TODO: validate duplicates here
+ if (credential.getUsername() == null || credential.getUsername().isBlank()) {
+ throw new IllegalArgumentException("Username cannot be blank");
+ }
+ if (credential.getEmail() == null || !credential.getEmail().matches("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$")) {
+ throw new IllegalArgumentException("Invalid email format");
+ }
+ if (credential.getPassword() == null || credential.getPassword().length() < 6) {
+ throw new IllegalArgumentException("Password must be at least 6 characters");
+ }
+ if (credential.getServiceId() == null || credential.getServiceId() < 0) {
+ throw new IllegalArgumentException("ServiceId must be positive");
+ }
+
+ boolean existsUsername = credentialRepository.existsByUsernameAndServiceId(
+ credential.getUsername(), credential.getServiceId());
+ if (existsUsername) throw new IllegalArgumentException("Username already exists for this service");
+
+ boolean existsEmail = credentialRepository.existsByEmailAndServiceId(
+ credential.getEmail(), credential.getServiceId());
+ if (existsEmail) throw new IllegalArgumentException("Email already exists for this service");
+
+ credential.setPassword(passwordEncoder.encode(credential.getPassword()));
return credentialRepository.save(credential);
}
+
+ public List getAll() {
+ return credentialRepository.findAll();
+ }
public List getByUserId(UUID userId) {
- List creds = credentialRepository.findByUserId(userId);
+ List creds = credentialRepository.findByUserId(UuidUtils.uuidToBin(userId));
if (creds.isEmpty()) {
throw new RuntimeException("User has no credentials");
}
return creds;
}
- public Credential getByUserIdAndService(short serviceId, String username) {
+ public User getByEmail(String email) {
+ return credentialRepository.findByEmail(email)
+ .orElseThrow(() -> new RuntimeException("No credential found for email"))
+ .getUser();
+ }
+
+ public Credential getByUserIdAndService(Byte serviceId, String username) {
return credentialRepository.findByServiceIdAndUsername(serviceId, username)
.orElseThrow(() -> new RuntimeException("Credential not found in this site"));
}
- public Credential getForLogin(short serviceId, String username) {
+ public Credential getForLogin(Byte serviceId, String username) {
return credentialRepository.findByServiceIdAndUsername(serviceId, username)
.orElseThrow(() -> new RuntimeException("Invalid credentials"));
}
+
+ public boolean existsByUsernameAndService(String username, int serviceId) {
+ return credentialRepository.findByUsernameAndServiceId(username, serviceId).isPresent();
+ }
+
+ public boolean isOwner(UUID credentialId, UUID userId) {
+ byte[] idBytes = UuidUtils.uuidToBin(credentialId);
+ Credential c = credentialRepository.findById(idBytes)
+ .orElseThrow(() -> new RuntimeException("Credential not found"));
+ return c.getUserId().equals(userId);
+ }
+
+ public Credential update(UUID credentialId, CredentialDto dto) {
+ byte[] idBytes = UuidUtils.uuidToBin(credentialId);
+
+ Credential cred = credentialRepository.findById(idBytes)
+ .orElseThrow(() -> new RuntimeException("Credential not found"));
+
+ if (dto.getUsername() != null && dto.getUsername().isBlank()) {
+ throw new IllegalArgumentException("Username cannot be blank");
+ }
+ if (dto.getEmail() != null && !dto.getEmail().matches("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$")) {
+ throw new IllegalArgumentException("Invalid email format");
+ }
+ if (dto.getServiceId() != null && dto.getServiceId() < 0) {
+ throw new IllegalArgumentException("ServiceId must be positive");
+ }
+
+ if (dto.getUsername() != null) cred.setUsername(dto.getUsername());
+ if (dto.getEmail() != null) cred.setEmail(dto.getEmail());
+ if (dto.getServiceId() != null) cred.setServiceId(dto.getServiceId());
+ if (dto.getStatus() != null) cred.setStatus(dto.getStatus());
+
+ return credentialRepository.save(cred);
+ }
+
+ public Credential updatePassword(UUID credentialId, ChangePasswordRequest request) {
+ byte[] idBytes = UuidUtils.uuidToBin(credentialId);
+
+ Credential cred = credentialRepository.findById(idBytes)
+ .orElseThrow(() -> new RuntimeException("Credential not found"));
+
+ if (!passwordEncoder.matches(request.getOldPassword(), cred.getPassword())) {
+ throw new IllegalArgumentException("Old password is incorrect");
+ }
+
+ cred.setPassword(passwordEncoder.encode(request.getNewPassword()));
+ return credentialRepository.save(cred);
+ }
+
+ public void delete(UUID credentialId) {
+ byte[] idBytes = UuidUtils.uuidToBin(credentialId);
+ if(!credentialRepository.existsById(idBytes))
+ throw new RuntimeException("Credential not found");
+ credentialRepository.deleteById(idBytes);
+ }
+
}
diff --git a/core/src/main/java/net/miarma/backend/core/service/FileService.java b/core/src/main/java/net/miarma/backend/core/service/FileService.java
index 91001a1..278efb3 100644
--- a/core/src/main/java/net/miarma/backend/core/service/FileService.java
+++ b/core/src/main/java/net/miarma/backend/core/service/FileService.java
@@ -1,6 +1,12 @@
package net.miarma.backend.core.service;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Service;
@@ -8,39 +14,79 @@ import org.springframework.transaction.annotation.Transactional;
import net.miarma.backend.core.model.File;
import net.miarma.backend.core.repository.FileRepository;
+import net.miarma.backend.core.util.UuidUtils;
@Service
@Transactional
public class FileService {
private final FileRepository fileRepository;
+ private final String basePath = "/var/www/files";
public FileService(FileRepository fileRepository) {
this.fileRepository = fileRepository;
}
- public File get(UUID fileId) {
- return fileRepository.findById(fileId)
+ public File getById(UUID fileId) {
+ byte[] idBytes = UuidUtils.uuidToBin(fileId);
+ return fileRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("File not found"));
}
- public List listByUser(UUID userId) {
+ public List getAll(Map params) {
+ if (params.containsKey("userId")) {
+ UUID userId = UUID.fromString(params.get("userId"));
+ return fileRepository.findByUploadedBy(userId);
+ }
+ if (params.containsKey("context")) {
+ short context = Short.parseShort(params.get("context"));
+ return fileRepository.findByContext(context);
+ }
+ return fileRepository.findAll();
+ }
+
+ public List getByUserId(UUID userId) {
return fileRepository.findByUploadedBy(userId);
}
- public List listByContext(short context) {
- return fileRepository.findByContext(context);
- }
+ public File create(File file, byte[] fileBinary) throws IOException {
+ Path dirPath = Paths.get(basePath, String.valueOf(file.getContext()));
+ if (!Files.exists(dirPath)) {
+ Files.createDirectories(dirPath);
+ }
+
+ Path filePath = dirPath.resolve(file.getFileName());
+ try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
+ fos.write(fileBinary);
+ }
+
+ file.setFilePath(filePath.toString());
- public File create(File file) {
return fileRepository.save(file);
}
- public void delete(UUID fileId) {
- if (!fileRepository.existsById(fileId)) {
+ public File update(File file) {
+ byte[] idBytes = UuidUtils.uuidToBin(file.getFileId());
+ if (!fileRepository.existsById(idBytes)) {
throw new RuntimeException("File not found");
}
- fileRepository.deleteById(fileId);
+ return fileRepository.save(file);
}
+
+ public void delete(UUID fileId) {
+ byte[] idBytes = UuidUtils.uuidToBin(fileId);
+ if (!fileRepository.existsById(idBytes)) {
+ throw new RuntimeException("File not found");
+ }
+ fileRepository.deleteById(idBytes);
+ }
+
+ public boolean isOwner(UUID fileId, UUID userId) {
+ byte[] fileBytes = UuidUtils.uuidToBin(fileId);
+ return fileRepository.findById(fileBytes)
+ .map(f -> f.getUploadedBy().equals(userId))
+ .orElse(false);
+ }
}
+
diff --git a/core/src/main/java/net/miarma/backend/core/service/JwtService.java b/core/src/main/java/net/miarma/backend/core/service/JwtService.java
index fa87a6e..291274c 100644
--- a/core/src/main/java/net/miarma/backend/core/service/JwtService.java
+++ b/core/src/main/java/net/miarma/backend/core/service/JwtService.java
@@ -16,7 +16,7 @@ public class JwtService {
private final long expiration = 3600_000;
- public String generateToken(UUID userId, short serviceId) {
+ public String generateToken(UUID userId, Byte serviceId) {
Date now = new Date();
Date exp = new Date(now.getTime() + expiration);
@@ -43,9 +43,9 @@ public class JwtService {
return UUID.fromString(claims.getSubject());
}
- public short getServiceId(String token) {
+ public Byte getServiceId(String token) {
Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
- return ((Number) claims.get("service")).shortValue();
+ return ((Number) claims.get("service")).byteValue();
}
public Date getExpiration(String token) {
diff --git a/core/src/main/java/net/miarma/backend/core/service/UserService.java b/core/src/main/java/net/miarma/backend/core/service/UserService.java
index a9b9626..805d139 100644
--- a/core/src/main/java/net/miarma/backend/core/service/UserService.java
+++ b/core/src/main/java/net/miarma/backend/core/service/UserService.java
@@ -1,12 +1,15 @@
package net.miarma.backend.core.service;
+import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
+import net.miarma.backend.core.dto.UserDto;
import net.miarma.backend.core.model.User;
import net.miarma.backend.core.repository.UserRepository;
+import net.miarma.backend.core.util.UuidUtils;
@Service
@Transactional
@@ -17,25 +20,59 @@ public class UserService {
this.userRepository = userRepository;
}
+ public List getAll() {
+ return userRepository.findAll();
+ }
+
public User getById(UUID userId) {
- return userRepository.findById(userId)
- .orElseThrow(() -> new RuntimeException("User not found"));
+ byte[] idBytes = UuidUtils.uuidToBin(userId);
+ return userRepository.findById(idBytes)
+ .orElseThrow(() -> new RuntimeException("User not found"));
}
- public User create(User user) {
- // TODO: basic validation
- return userRepository.save(user);
- }
-
- public User update(User user) {
- if(!userRepository.existsById(user.getUserId()))
- throw new RuntimeException("User not found");
- return userRepository.save(user);
- }
+ public User create(UserDto dto) {
+ if(dto.getDisplayName() == null || dto.getDisplayName().isBlank()) {
+ throw new RuntimeException("Display name is required");
+ }
+
+ User user = new User();
+ user.setUserId(UUID.randomUUID());
+ user.setDisplayName(dto.getDisplayName());
+ user.setAvatar(dto.getAvatar());
+ user.setGlobalRole(dto.getGlobalRole() != null ? dto.getGlobalRole() : 0);
+ user.setGlobalStatus(dto.getGlobalStatus() != null ? dto.getGlobalStatus() : 1);
+
+ return userRepository.save(user);
+ }
+
+ public User update(UUID userId, UserDto dto) {
+ byte[] idBytes = UuidUtils.uuidToBin(userId);
+ User user = userRepository.findById(idBytes)
+ .orElseThrow(() -> new RuntimeException("User not found"));
+
+ if (dto.getDisplayName() != null) {
+ String displayName = dto.getDisplayName().trim();
+ if (displayName.isEmpty()) {
+ throw new IllegalArgumentException("Display name cannot be empty");
+ }
+ if (displayName.length() > 50) {
+ throw new IllegalArgumentException("Display name too long (max 50 chars)");
+ }
+ user.setDisplayName(displayName);
+ }
+
+ if(dto.getDisplayName() != null) user.setDisplayName(dto.getDisplayName());
+ if(dto.getAvatar() != null) user.setAvatar(dto.getAvatar());
+ if(dto.getGlobalRole() != null) user.setGlobalRole(dto.getGlobalRole());
+ if(dto.getGlobalStatus() != null) user.setGlobalStatus(dto.getGlobalStatus());
+
+ return userRepository.save(user);
+ }
public void delete(UUID userId) {
- if(!userRepository.existsById(userId))
+ byte[] idBytes = UuidUtils.uuidToBin(userId);
+ if(!userRepository.existsById(idBytes))
throw new RuntimeException("User not found");
- userRepository.deleteById(userId);
+ userRepository.deleteById(idBytes);
}
}
diff --git a/core/src/main/java/net/miarma/backend/core/util/UuidUtils.java b/core/src/main/java/net/miarma/backend/core/util/UuidUtils.java
new file mode 100644
index 0000000..4cbf4cd
--- /dev/null
+++ b/core/src/main/java/net/miarma/backend/core/util/UuidUtils.java
@@ -0,0 +1,21 @@
+package net.miarma.backend.core.util;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+public class UuidUtils {
+ public static byte[] uuidToBin(UUID uuid) {
+ ByteBuffer bb = ByteBuffer.allocate(16);
+ bb.putLong(uuid.getMostSignificantBits());
+ bb.putLong(uuid.getLeastSignificantBits());
+ return bb.array();
+ }
+
+ public static UUID binToUUID(byte[] bin) {
+ ByteBuffer bb = ByteBuffer.wrap(bin);
+ long high = bb.getLong();
+ long low = bb.getLong();
+ return new UUID(high, low);
+ }
+
+}
diff --git a/core/src/main/resources/application.yml b/core/src/main/resources/application.yml
index 76c7404..092861e 100644
--- a/core/src/main/resources/application.yml
+++ b/core/src/main/resources/application.yml
@@ -1,12 +1,47 @@
+server:
+ port: 8080
+ servlet:
+ context-path: /v2/core
+
spring:
+ application:
+ name: core-service
+
datasource:
url: jdbc:mariadb://localhost:3306/miarma_v2
username: admin
password: ositovito
+ driver-class-name: org.mariadb.jdbc.Driver
+
jpa:
+ open-in-view: false
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
+ jdbc:
+ time_zone: UTC
+ dialect: org.hibernate.dialect.MariaDBDialect
+
+ jackson:
+ serialization:
+ INDENT_OUTPUT: true
+ date-format: yyyy-MM-dd'T'HH:mm:ss
+ time-zone: Europe/Madrid
+ default-property-inclusion: non_null
+ generator:
+ WRITE_DATES_AS_TIMESTAMPS: false
+
+logging:
+ level:
+ org.hibernate.SQL: DEBUG
+ org.hibernate.orm.jdbc.bind: TRACE
+ org.springframework.security: INFO
+
+management:
+ endpoints:
+ web:
+ exposure:
+ include: health,info
diff --git a/target/.gitignore b/target/.gitignore
new file mode 100644
index 0000000..751bd89
--- /dev/null
+++ b/target/.gitignore
@@ -0,0 +1 @@
+/test-classes/
diff --git a/target/classes/META-INF/MANIFEST.MF b/target/classes/META-INF/MANIFEST.MF
deleted file mode 100644
index a5622f8..0000000
--- a/target/classes/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,4 +0,0 @@
-Manifest-Version: 1.0
-Build-Jdk-Spec: 21
-Created-By: Maven Integration for Eclipse
-
diff --git a/target/classes/META-INF/maven/net.miarma/backend/pom.properties b/target/classes/META-INF/maven/net.miarma/backend/pom.properties
deleted file mode 100644
index e27ed27..0000000
--- a/target/classes/META-INF/maven/net.miarma/backend/pom.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-#Generated by Maven Integration for Eclipse
-#Fri Jan 16 02:26:12 CET 2026
-artifactId=backend
-groupId=net.miarma
-m2e.projectLocation=/home/jomaa/git/miarma-backend
-m2e.projectName=backend
-version=1.0.0
diff --git a/target/classes/META-INF/maven/net.miarma/backend/pom.xml b/target/classes/META-INF/maven/net.miarma/backend/pom.xml
deleted file mode 100644
index 3b1fee6..0000000
--- a/target/classes/META-INF/maven/net.miarma/backend/pom.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
- 4.0.0
- net.miarma
- backend
- 1.0.0
-
\ No newline at end of file