diff --git a/pom.xml b/pom.xml
index c664f71..c5855e0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,185 +1,153 @@
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 3.4.4
-
-
- com.skycrate.backend
- skycrateBackend
- 0.0.1-SNAPSHOT
- skycrateBackend
- Cloud Storage App using HDFS
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 17
-
-
-
- org.springframework.boot
- spring-boot-starter
-
+
+ 4.0.0
-
- org.projectlombok
- lombok
- true
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.4
+
+
+ com.skycrate.backend
+ skycrateBackend
+ 0.0.1-SNAPSHOT
+ skycrateBackend
+ Cloud Storage App using HDFS
-
-
-
- org.springframework.boot
- spring-boot-starter-web
- 3.2.4
-
+
+ 17
+
-
-
- org.springframework.boot
- spring-boot-starter-security
- 3.2.4
-
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
- 3.2.4
-
+
+
+ com.mysql
+ mysql-connector-j
+ 8.3.0
+
-
-
- com.mysql
- mysql-connector-j
- 8.3.0
-
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+ runtime
+
-
-
- org.springframework.boot
- spring-boot-starter-validation
- 3.2.4
-
+
+
+ org.apache.hadoop
+ hadoop-common
+ 3.4.1
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
+
+ org.apache.hadoop
+ hadoop-hdfs
+ 3.4.1
+
+
+ org.apache.hadoop
+ hadoop-client
+ 3.4.1
+
-
-
- io.jsonwebtoken
- jjwt-api
- 0.11.5
-
-
- io.jsonwebtoken
- jjwt-impl
- 0.11.5
-
-
- io.jsonwebtoken
- jjwt-jackson
- 0.11.5
-
+
+
+ commons-io
+ commons-io
+ 2.11.0
+
-
-
- org.apache.hadoop
- hadoop-common
- 3.4.1
-
-
- org.slf4j
- slf4j-log4j12
-
-
-
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+ 3.1.8
+
-
- org.apache.hadoop
- hadoop-hdfs
- 3.4.1
-
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
-
- org.apache.hadoop
- hadoop-client
- 3.4.1
-
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- commons-io
- commons-io
- 2.11.0
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
-
- com.github.ben-manes.caffeine
- caffeine
- 3.1.8
-
-
-
-
-
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ ${java.version}
+ ${java.version}
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/config/HDFSConfig.java b/src/main/java/com/skycrate/backend/skycrateBackend/config/HDFSConfig.java
index 56f6b99..706490f 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/config/HDFSConfig.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/config/HDFSConfig.java
@@ -1,6 +1,6 @@
package com.skycrate.backend.skycrateBackend.config;
-import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.conf.Configuration; // Hadoop Configuration
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.security.UserGroupInformation;
import org.springframework.context.annotation.Bean;
@@ -8,20 +8,17 @@ import org.springframework.context.annotation.Bean;
import java.net.URI;
import java.security.PrivilegedExceptionAction;
- // HDFS configuration bean to securely connect to a remote Hadoop cluster.
-@Configuration
+@org.springframework.context.annotation.Configuration
public class HDFSConfig {
- private static final String HDFS_URI = System.getenv("HDFS_URI"); // export HDFS_URI=hdfs://192.168.29.30:9000
- private static final String HDFS_USER = System.getenv("HDFS_USER"); // Hadoop user (if needed)
+ private static final String HDFS_URI = System.getenv("HDFS_URI"); // e.g., hdfs://namenode:9000
+ private static final String HDFS_USER = System.getenv("HDFS_USER"); // e.g., hdfsuser
- // Configures and returns a secured HDFS FileSystem instance.
@Bean
public FileSystem fileSystem() throws Exception {
- return getHDFS(); // use the static method internally
+ return getHDFS();
}
- // Static method to get a FileSystem instance. Used by other classes like HDFSController.
public static FileSystem getHDFS() throws Exception {
if (HDFS_URI == null || HDFS_URI.isBlank()) {
throw new IllegalStateException("HDFS_URI environment variable not set.");
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/config/HttpToHttpsRedirectConfig.java b/src/main/java/com/skycrate/backend/skycrateBackend/config/HttpToHttpsRedirectConfig.java
index c7f3184..aef8553 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/config/HttpToHttpsRedirectConfig.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/config/HttpToHttpsRedirectConfig.java
@@ -14,10 +14,10 @@ public class HttpToHttpsRedirectConfig {
return factory -> {
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
- connector.setPort(8080); // HTTP port
+ connector.setPort(8085); // HTTP port
connector.setSecure(false);
connector.setRedirectPort(8443); // HTTPS port
factory.addAdditionalTomcatConnectors(connector);
};
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/config/JwtAuthenticationFilter.java b/src/main/java/com/skycrate/backend/skycrateBackend/config/JwtAuthenticationFilter.java
deleted file mode 100644
index 024b6c1..0000000
--- a/src/main/java/com/skycrate/backend/skycrateBackend/config/JwtAuthenticationFilter.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.skycrate.backend.skycrateBackend.config;
-
-import java.io.IOException;
-
-import org.springframework.lang.NonNull;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import org.springframework.stereotype.Component;
-import org.springframework.web.filter.OncePerRequestFilter;
-import org.springframework.web.servlet.HandlerExceptionResolver;
-
-import com.skycrate.backend.skycrateBackend.services.JwtService;
-
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-@Component
-public class JwtAuthenticationFilter extends OncePerRequestFilter {
-
-
- private final HandlerExceptionResolver handlerExceptionResolver;
- private JwtService jwtService;
- private UserDetailsService userDetailsService;
-
- public JwtAuthenticationFilter(JwtService jwtService,UserDetailsService userDetailsService,HandlerExceptionResolver handlerExceptionResolver){
-
- this.handlerExceptionResolver=handlerExceptionResolver;
- this.jwtService=jwtService;
- this.userDetailsService=userDetailsService;
- }
-
- @Override
- protected void doFilterInternal(
- @NonNull HttpServletRequest request,
- @NonNull HttpServletResponse response,
- @NonNull FilterChain filterChain) throws ServletException, IOException {
- final String authHeader=request.getHeader("Authorization");
- if (authHeader==null || !authHeader.startsWith("Bearer")){
- filterChain.doFilter(request, response);
- return;
- }
- try {
- final String userjwt=authHeader.substring(7);
- final String userEmail=jwtService.extractUsername(userjwt);
- Authentication authentication=SecurityContextHolder.getContext().getAuthentication();
- if(userEmail!=null && authentication==null){
-
- UserDetails userDetails=this.userDetailsService.loadUserByUsername(userEmail);
- if (jwtService.isTokenValid(userjwt, userDetails)) {
-
- UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(
- userDetails, null, userDetails.getAuthorities()
- );
- authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
- SecurityContextHolder.getContext().setAuthentication(authenticationToken);
- }
-
- }
- filterChain.doFilter(request, response);
- }
- catch (Exception err) {
- handlerExceptionResolver.resolveException(request, response, null, err);
- }
-
- }
-
-
-}
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/config/SecurityConfig.java b/src/main/java/com/skycrate/backend/skycrateBackend/config/SecurityConfig.java
index bdd4fc5..7cabb16 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/config/SecurityConfig.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/config/SecurityConfig.java
@@ -29,7 +29,7 @@ public class SecurityConfig {
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.authorizeHttpRequests(auth -> auth
- .requestMatchers("/api/auth/**").permitAll()
+ .requestMatchers("/api/auth/**", "/actuator/**").permitAll()
.requestMatchers(HttpMethod.GET, "/public/**").permitAll()
.anyRequest().authenticated()
)
@@ -41,7 +41,8 @@ public class SecurityConfig {
.includeSubDomains(true)
.maxAgeInSeconds(31536000)
)
- .xssProtection(xss -> xss.block(true))
+ // Spring Security 6+ no longer supports xss.block(true), so we just enable or disable it.
+ .xssProtection(xss -> xss.disable())
.frameOptions(frame -> frame.deny())
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/dto/RegisterUserDto.java b/src/main/java/com/skycrate/backend/skycrateBackend/dto/RegisterUserDto.java
index da001fd..484e35f 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/dto/RegisterUserDto.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/dto/RegisterUserDto.java
@@ -1,46 +1,46 @@
package com.skycrate.backend.skycrateBackend.dto;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+
public class RegisterUserDto {
-
+
+ @NotBlank(message = "Email is required")
+ @Email(message = "Email should be valid")
private String email;
+
+ @NotBlank(message = "Password is required")
+ @Size(min = 8, message = "Password must be at least 8 characters")
private String password;
+
+ @NotBlank(message = "Username is required")
+ @Pattern(regexp = "^[a-zA-Z0-9]+$", message = "Username must be alphanumeric")
+ private String username;
+
+ @NotBlank(message = "Full name is required")
+ private String fullname;
+
+ @NotBlank(message = "First name is required")
private String firstname;
+
+ @NotBlank(message = "Last name is required")
private String lastname;
- public String getEmail() {
- return email;
- }
+ // Getters
+ public String getEmail() { return email; }
+ public String getPassword() { return password; }
+ public String getUsername() { return username; }
+ public String getFullname() { return fullname; }
+ public String getFirstname() { return firstname; }
+ public String getLastname() { return lastname; }
- public RegisterUserDto setEmail(String email) {
- this.email = email;
- return this;
- }
-
- public String getPassword() {
- return password;
- }
-
- public RegisterUserDto setPassword(String password) {
- this.password = password;
- return this;
-
- }
-
- public String getFirstname() {
- return firstname;
- }
-
- public RegisterUserDto setFirstname(String firstname) {
- this.firstname = firstname;
- return this;
- }
-
- public String getLastname() {
- return lastname;
- }
-
- public RegisterUserDto setLastname(String lastname) {
- this.lastname = lastname;
- return this;
- }
-}
+ // Setters
+ public void setEmail(String email) { this.email = email; }
+ public void setPassword(String password) { this.password = password; }
+ public void setUsername(String username) { this.username = username; }
+ public void setFullname(String fullname) { this.fullname = fullname; }
+ public void setFirstname(String firstname) { this.firstname = firstname; }
+ public void setLastname(String lastname) { this.lastname = lastname; }
+}
\ No newline at end of file
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/entity/FileMetadata.java b/src/main/java/com/skycrate/backend/skycrateBackend/entity/FileMetadata.java
index e74d582..a077276 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/entity/FileMetadata.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/entity/FileMetadata.java
@@ -1,23 +1,43 @@
package com.skycrate.backend.skycrateBackend.entity;
import jakarta.persistence.*;
+import lombok.*;
@Entity
+@Table(name = "file_metadata")
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
public class FileMetadata {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
+ @Column(nullable = false)
private String filePath;
+ @Column(nullable = false)
private String username;
@Lob
+ @Column(nullable = false)
private byte[] salt;
@Lob
+ @Column(nullable = false)
private byte[] iv;
- // Getters and Setters
+ public void setUsername(String username) { this.username = username; }
+ public void setFilePath(String filePath) { this.filePath = filePath; }
+ public void setSalt(byte[] salt) { this.salt = salt; }
+ public void setIv(byte[] iv) { this.iv = iv; }
+
+ public String getUsername() { return this.username; }
+ public String getFilePath() { return this.filePath; }
+ public byte[] getSalt() { return this.salt; }
+ public byte[] getIv() { return this.iv; }
+
}
\ No newline at end of file
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/entity/User.java b/src/main/java/com/skycrate/backend/skycrateBackend/entity/User.java
index bde3655..7138eaa 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/entity/User.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/entity/User.java
@@ -14,7 +14,6 @@ import java.util.List;
@Setter
@NoArgsConstructor
@AllArgsConstructor
-@Builder
public class User implements UserDetails {
@Id
@@ -39,6 +38,16 @@ public class User implements UserDetails {
@Lob
private byte[] privateKey;
+ @Builder
+ public User(String email, String password, String username, String fullname, byte[] publicKey, byte[] privateKey) {
+ this.email = email;
+ this.password = password;
+ this.username = username;
+ this.fullname = fullname;
+ this.publicKey = publicKey;
+ this.privateKey = privateKey;
+ }
+
// --- UserDetails interface methods ---
@Override
public Collection extends GrantedAuthority> getAuthorities() {
@@ -74,45 +83,4 @@ public class User implements UserDetails {
public boolean isEnabled() {
return true;
}
-
- // --- Extra getter methods for HDFScontroller compatibility ---
- public byte[] getPublicKey() {
- return publicKey;
- }
-
- public byte[] getPrivateKey() {
- return privateKey;
- }
-
- public String getFullname() {
- return fullname;
- }
-
- public Long getId() {
- return id;
- }
-
- 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;
- }
}
\ No newline at end of file
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/security/JwtAuthenticationFilter.java b/src/main/java/com/skycrate/backend/skycrateBackend/security/JwtAuthenticationFilter.java
index 532743b..38b9e37 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/security/JwtAuthenticationFilter.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/security/JwtAuthenticationFilter.java
@@ -37,6 +37,12 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
FilterChain filterChain)
throws ServletException, IOException {
+ String path = request.getRequestURI();
+ if (path.startsWith("/api/auth") || path.startsWith("/actuator")) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+
final String authHeader = request.getHeader("Authorization");
final String jwt;
final String userEmail;
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java b/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java
deleted file mode 100644
index 7aad7ee..0000000
--- a/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.skycrate.backend.skycrateBackend.security;
-
-import com.github.benmanes.caffeine.cache.Cache;
-import com.github.benmanes.caffeine.cache.Caffeine;
-import org.springframework.stereotype.Service;
-
-import java.util.concurrent.TimeUnit;
-
-@Service
-public class RateLimiterService {
-
- private final Cache attemptsCache;
-
- private static final int MAX_ATTEMPTS = 5;
-
- public RateLimiterService() {
- this.attemptsCache = Caffeine.newBuilder()
- .expireAfterWrite(1, TimeUnit.MINUTES)
- .build();
- }
-
- public boolean isBlocked(String key) {
- Integer attempts = attemptsCache.getIfPresent(key);
- return attempts != null && attempts >= MAX_ATTEMPTS;
- }
-
- public void recordFailedAttempt(String key) {
- int attempts = attemptsCache.getIfPresent(key) == null ? 0 : attemptsCache.getIfPresent(key);
- attemptsCache.put(key, attempts + 1);
- }
-
- public void resetAttempts(String key) {
- attemptsCache.invalidate(key);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/services/AuthenticationService.java b/src/main/java/com/skycrate/backend/skycrateBackend/services/AuthenticationService.java
index ff333ba..7cea86d 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/services/AuthenticationService.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/services/AuthenticationService.java
@@ -1,15 +1,14 @@
package com.skycrate.backend.skycrateBackend.services;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.stereotype.Service;
-
import com.skycrate.backend.skycrateBackend.dto.LoginUserDto;
import com.skycrate.backend.skycrateBackend.dto.RegisterUserDto;
import com.skycrate.backend.skycrateBackend.entity.User;
import com.skycrate.backend.skycrateBackend.repository.UserRepository;
import com.skycrate.backend.skycrateBackend.utils.RSAKeyUtil;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
@@ -21,28 +20,31 @@ public class AuthenticationService {
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
- public AuthenticationService(UserRepository userRepository, AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder) {
+ public AuthenticationService(UserRepository userRepository,
+ AuthenticationManager authenticationManager,
+ PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.authenticationManager = authenticationManager;
}
public User signUp(RegisterUserDto inputUser) {
- User user = new User(
- inputUser.getFirstname(),
- inputUser.getLastname(),
- inputUser.getEmail(),
- passwordEncoder.encode(inputUser.getPassword())
- );
-
+ KeyPair keyPair;
try {
- KeyPair keyPair = RSAKeyUtil.generateKeyPair();
- user.setPublicKey(keyPair.getPublic().getEncoded());
- user.setPrivateKey(keyPair.getPrivate().getEncoded());
+ keyPair = RSAKeyUtil.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Failed to generate RSA key pair", e);
}
+ User user = User.builder()
+ .fullname(inputUser.getFirstname() + " " + inputUser.getLastname())
+ .username(inputUser.getUsername())
+ .email(inputUser.getEmail())
+ .password(passwordEncoder.encode(inputUser.getPassword()))
+ .publicKey(keyPair.getPublic().getEncoded())
+ .privateKey(keyPair.getPrivate().getEncoded())
+ .build();
+
return userRepository.save(user);
}