Compare commits
10 Commits
2622667de4
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
490578cfe2
|
|||
|
c45bc27c81
|
|||
|
95d77fb3fe
|
|||
|
92b335410b
|
|||
|
b7ce85a5ec
|
|||
|
7ae2eca31b
|
|||
|
0aba0e7911
|
|||
|
7411f8b4fa
|
|||
| b2147537ca | |||
| 063bfa794a |
@@ -1,3 +1,4 @@
|
|||||||
|
src/main/resources/application.properties.bak
|
||||||
wiki/
|
wiki/
|
||||||
HELP.md
|
HELP.md
|
||||||
target/
|
target/
|
||||||
|
|||||||
+5
-8
@@ -5,7 +5,7 @@ FROM debian:12-slim
|
|||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
LABEL maintainer="kshitijka"
|
LABEL maintainer="kshitijka"
|
||||||
LABEL version=1.0
|
LABEL version=1.5
|
||||||
LABEL description="Skycrate is a web based file management system that uses Hadoop as filesystem."
|
LABEL description="Skycrate is a web based file management system that uses Hadoop as filesystem."
|
||||||
|
|
||||||
# Update & upgrade & install & rm
|
# Update & upgrade & install & rm
|
||||||
@@ -19,16 +19,13 @@ RUN useradd -s /bin/bash skycrateBack
|
|||||||
# Create work dir
|
# Create work dir
|
||||||
RUN mkdir /app
|
RUN mkdir /app
|
||||||
RUN chown -R skycrateBack:skycrateBack /app
|
RUN chown -R skycrateBack:skycrateBack /app
|
||||||
COPY ./target/ /app
|
COPY ./target/skycrateBackend-0.0.2.jar /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Create temp download directory
|
|
||||||
RUN mkdir -p /Skycrate/downloaded/
|
|
||||||
RUN chown -R skycrateBack:skycrateBack /Skycrate /Skycrate/downloaded/
|
|
||||||
|
|
||||||
# Switch user
|
# Switch user
|
||||||
USER skycrateBack
|
USER skycrateBack
|
||||||
|
|
||||||
EXPOSE 8081
|
# Expose port for backend
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
CMD ["java", "-jar", "/app/skycrateBackend-0.0.1-SNAPSHOT.jar"]
|
CMD ["java", "-jar", "skycrateBackend-0.0.2.jar"]
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Skycrate-Backend
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This repository holds code for [Skycrate](https://git.kska.io/notkshitij/Skycrate) backend.
|
||||||
|
|
||||||
|
---
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
<groupId>com.skycrate.backend</groupId>
|
<groupId>com.skycrate.backend</groupId>
|
||||||
<artifactId>skycrateBackend</artifactId>
|
<artifactId>skycrateBackend</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2</version>
|
||||||
<name>skycrateBackend</name>
|
<name>skycrateBackend</name>
|
||||||
<description>Cloud Storage App using HDFS</description>
|
<description>Cloud Storage App using HDFS</description>
|
||||||
|
|
||||||
@@ -121,6 +121,17 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Caching -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
<version>3.0.5</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.skycrate.backend.skycrateBackend.config;
|
||||||
|
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
public class CacheConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CaffeineCacheManager cacheManager() {
|
||||||
|
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||||
|
cacheManager.setCaffeine(Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(30, TimeUnit.MINUTES) // Cache expiry time
|
||||||
|
.maximumSize(100)); // Maximum cache size
|
||||||
|
return cacheManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public class SecurityConfig {
|
|||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authenticationProvider(authenticationProvider)
|
.authenticationProvider(authenticationProvider)
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("/api/auth/login", "/api/auth/register", "/actuator/**").permitAll()
|
.requestMatchers("/api/auth/logout","/api/auth/login", "/api/auth/register", "/actuator/**").permitAll()
|
||||||
.requestMatchers(HttpMethod.GET, "/public/**").permitAll()
|
.requestMatchers(HttpMethod.GET, "/public/**").permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ import com.skycrate.backend.skycrateBackend.entity.RefreshToken;
|
|||||||
import com.skycrate.backend.skycrateBackend.entity.User;
|
import com.skycrate.backend.skycrateBackend.entity.User;
|
||||||
import com.skycrate.backend.skycrateBackend.repository.UserRepository;
|
import com.skycrate.backend.skycrateBackend.repository.UserRepository;
|
||||||
import com.skycrate.backend.skycrateBackend.security.TokenBlacklistService;
|
import com.skycrate.backend.skycrateBackend.security.TokenBlacklistService;
|
||||||
import com.skycrate.backend.skycrateBackend.services.AuthenticationService;
|
import com.skycrate.backend.skycrateBackend.services.*;
|
||||||
import com.skycrate.backend.skycrateBackend.services.JwtService;
|
|
||||||
import com.skycrate.backend.skycrateBackend.services.RateLimiterService;
|
|
||||||
import com.skycrate.backend.skycrateBackend.services.RefreshTokenService;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
@@ -23,6 +22,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
@RequestMapping("/api/auth")
|
@RequestMapping("/api/auth")
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(FileService.class);
|
||||||
private final AuthenticationManager authManager;
|
private final AuthenticationManager authManager;
|
||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
@@ -92,29 +92,43 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String token = authHeader.substring(7);
|
String token = authHeader.substring(7);
|
||||||
|
String username = jwtService.extractUsername(token);
|
||||||
|
|
||||||
|
userRepository.findByUsername(username).ifPresent(user -> {
|
||||||
|
// Clear the cached decrypted private key for the user
|
||||||
|
authenticationService.clearDecryptedPrivateKeyCache(user.getId().toString());
|
||||||
|
|
||||||
|
// Delete the refresh token associated with the user
|
||||||
|
refreshTokenService.logout(user); // This should delete the token
|
||||||
|
});
|
||||||
|
|
||||||
tokenBlacklistService.blacklistToken(token);
|
tokenBlacklistService.blacklistToken(token);
|
||||||
|
|
||||||
String email = jwtService.extractUsername(token);
|
|
||||||
userRepository.findByEmail(email).ifPresent(refreshTokenService::deleteByUser);
|
|
||||||
|
|
||||||
return ResponseEntity.ok("Logged out successfully");
|
return ResponseEntity.ok("Logged out successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/refresh")
|
@PostMapping("/refresh")
|
||||||
public ResponseEntity<?> refresh(@RequestBody TokenRefreshRequest request) {
|
public ResponseEntity<?> refresh(@RequestBody TokenRefreshRequest request) {
|
||||||
String requestToken = request.getRefreshToken();
|
String requestToken = request.getRefreshToken();
|
||||||
|
log.error("Received refresh token: " + requestToken);
|
||||||
|
|
||||||
return refreshTokenService.findByToken(requestToken)
|
return refreshTokenService.findByToken(requestToken)
|
||||||
.map(token -> {
|
.map(token -> {
|
||||||
if (refreshTokenService.isExpired(token)) {
|
if (refreshTokenService.isExpired(token)) {
|
||||||
|
log.error("Refresh token expired for user: " + token.getUser().getUsername());
|
||||||
|
// Clear the cached key on token expiry
|
||||||
|
authenticationService.clearDecryptedPrivateKeyCache(token.getUser().getId().toString());
|
||||||
return ResponseEntity.status(403).body("Refresh token expired");
|
return ResponseEntity.status(403).body("Refresh token expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
User user = token.getUser();
|
User user = token.getUser();
|
||||||
String newAccessToken = jwtService.generateToken(user);
|
String newAccessToken = jwtService.generateToken(user);
|
||||||
|
log.info("Generated new access token for user: " + user.getUsername());
|
||||||
return ResponseEntity.ok(new TokenRefreshResponse(newAccessToken, requestToken));
|
return ResponseEntity.ok(new TokenRefreshResponse(newAccessToken, requestToken));
|
||||||
})
|
})
|
||||||
.orElseGet(() -> ResponseEntity.status(403).body("Invalid refresh token"));
|
.orElseGet(() -> {
|
||||||
|
log.error("Invalid refresh token: " + requestToken);
|
||||||
|
return ResponseEntity.status(403).body("Invalid refresh token");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.skycrate.backend.skycrateBackend.controller;
|
package com.skycrate.backend.skycrateBackend.controller;
|
||||||
|
|
||||||
|
import com.skycrate.backend.skycrateBackend.dto.FileDownloadRequest;
|
||||||
import com.skycrate.backend.skycrateBackend.services.FileService;
|
import com.skycrate.backend.skycrateBackend.services.FileService;
|
||||||
import com.skycrate.backend.skycrateBackend.services.JwtService;
|
import com.skycrate.backend.skycrateBackend.services.JwtService;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
@@ -39,20 +40,20 @@ public class FileController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/download/{filename}")
|
@GetMapping("/download")
|
||||||
public ResponseEntity<?> downloadFile(
|
public ResponseEntity<?> downloadFile(
|
||||||
@PathVariable String filename,
|
@RequestBody FileDownloadRequest fileDownloadRequest,
|
||||||
@RequestParam("password") String password,
|
|
||||||
HttpServletRequest request
|
HttpServletRequest request
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
String token = extractToken(request);
|
String token = extractToken(request);
|
||||||
String username = jwtService.extractUsername(token);
|
String username = jwtService.extractUsername(token);
|
||||||
|
|
||||||
byte[] decryptedData = fileService.downloadDecryptedFile(username, password, filename);
|
// Use the password and filename from the FileDownloadRequest DTO
|
||||||
|
byte[] decryptedData = fileService.downloadDecryptedFile(username, fileDownloadRequest.getPassword(), fileDownloadRequest.getFilename());
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileDownloadRequest.getFilename() + "\"")
|
||||||
.contentLength(decryptedData.length)
|
.contentLength(decryptedData.length)
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.body(decryptedData);
|
.body(decryptedData);
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.skycrate.backend.skycrateBackend.dto;
|
||||||
|
|
||||||
|
public class FileDownloadRequest {
|
||||||
|
private String filename;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilename(String filename) {
|
||||||
|
this.filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -15,4 +15,5 @@ public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long
|
|||||||
@Modifying
|
@Modifying
|
||||||
@Query("DELETE FROM RefreshToken t WHERE t.user = :user")
|
@Query("DELETE FROM RefreshToken t WHERE t.user = :user")
|
||||||
void deleteByUser(User user);
|
void deleteByUser(User user);
|
||||||
|
|
||||||
}
|
}
|
||||||
+29
-1
@@ -9,6 +9,8 @@ import com.skycrate.backend.skycrateBackend.utils.EncryptionUtil;
|
|||||||
import com.skycrate.backend.skycrateBackend.utils.RSAKeyUtil;
|
import com.skycrate.backend.skycrateBackend.utils.RSAKeyUtil;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
@@ -17,6 +19,8 @@ import org.springframework.stereotype.Service;
|
|||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AuthenticationService {
|
public class AuthenticationService {
|
||||||
@@ -24,13 +28,18 @@ public class AuthenticationService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
|
private final KeyCacheService keyCacheService;
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AuthenticationService.class);
|
||||||
|
|
||||||
public AuthenticationService(UserRepository userRepository,
|
public AuthenticationService(UserRepository userRepository,
|
||||||
AuthenticationManager authenticationManager,
|
AuthenticationManager authenticationManager,
|
||||||
PasswordEncoder passwordEncoder) {
|
PasswordEncoder passwordEncoder,
|
||||||
|
KeyCacheService keyCacheService) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
this.authenticationManager = authenticationManager;
|
this.authenticationManager = authenticationManager;
|
||||||
|
this.keyCacheService = keyCacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User signUp(RegisterUserDto inputUser) {
|
public User signUp(RegisterUserDto inputUser) {
|
||||||
@@ -90,4 +99,23 @@ public class AuthenticationService {
|
|||||||
return userRepository.findByEmail(inputUser.getEmail())
|
return userRepository.findByEmail(inputUser.getEmail())
|
||||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
.orElseThrow(() -> new RuntimeException("User not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Cacheable(value = "decryptedPrivateKeys", key = "#userId")
|
||||||
|
public byte[] getDecryptedPrivateKey(String userId, String password) throws Exception {
|
||||||
|
User user = userRepository.findById(Integer.valueOf(userId))
|
||||||
|
.orElseThrow(() -> new RuntimeException("User not found: " + userId));
|
||||||
|
|
||||||
|
log.info("Caching decrypted private key for userId: {}", userId);
|
||||||
|
|
||||||
|
SecretKey derivedKey = EncryptionUtil.deriveKey(password.toCharArray(), user.getPrivateKeySalt());
|
||||||
|
byte[] decryptedPrivateKeyBytes = EncryptionUtil.decrypt(user.getPrivateKey(), derivedKey, user.getPrivateKeyIv());
|
||||||
|
return decryptedPrivateKeyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict(value = "decryptedPrivateKeys", key = "#userId")
|
||||||
|
public void clearDecryptedPrivateKeyCache(String userId) {
|
||||||
|
// This method will clear the cached decrypted private key for the given userId
|
||||||
|
log.info("Clearing Caching decrypted private key for userId: {}", userId);
|
||||||
|
keyCacheService.clearKey(Long.valueOf(userId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,63 +17,63 @@ public class EncryptionUtil {
|
|||||||
private static final int IV_LENGTH = 16; // for AES CBC
|
private static final int IV_LENGTH = 16; // for AES CBC
|
||||||
private static final int ITERATIONS = 65536;
|
private static final int ITERATIONS = 65536;
|
||||||
private static final int KEY_LENGTH = 256; // bits
|
private static final int KEY_LENGTH = 256; // bits
|
||||||
//
|
|
||||||
// // --- AES key derivation using PBKDF2 ---
|
// --- AES key derivation using PBKDF2 ---
|
||||||
// public static SecretKey deriveAESKey(char[] password, byte[] salt)
|
public static SecretKey deriveAESKey(char[] password, byte[] salt)
|
||||||
// throws NoSuchAlgorithmException, InvalidKeySpecException {
|
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
//
|
|
||||||
// SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||||
//
|
|
||||||
// KeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
|
KeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
|
||||||
// byte[] keyBytes = factory.generateSecret(spec).getEncoded();
|
byte[] keyBytes = factory.generateSecret(spec).getEncoded();
|
||||||
//
|
|
||||||
// return new SecretKeySpec(keyBytes, "AES");
|
return new SecretKeySpec(keyBytes, "AES");
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// // --- Encrypt data using AES-CBC ---
|
// --- Encrypt data using AES-CBC ---
|
||||||
// public static byte[] encrypt(byte[] data, SecretKey key, byte[] iv)
|
public static byte[] encrypt(byte[] data, SecretKey key, byte[] iv)
|
||||||
// throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
//
|
|
||||||
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
//
|
|
||||||
// IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
// cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
|
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
|
||||||
//
|
|
||||||
// return cipher.doFinal(data);
|
return cipher.doFinal(data);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// --- Decrypt data using AES-CBC ---
|
// --- Decrypt data using AES-CBC ---
|
||||||
// public static byte[] decrypt(byte[] encryptedData, SecretKey key, byte[] iv)
|
public static byte[] decrypt(byte[] encryptedData, SecretKey key, byte[] iv)
|
||||||
// throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
//
|
|
||||||
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
||||||
//
|
|
||||||
// IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
// cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
|
|
||||||
//
|
|
||||||
// return cipher.doFinal(encryptedData);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // --- Generate random salt ---
|
|
||||||
// public static byte[] generateSalt() {
|
|
||||||
// byte[] salt = new byte[SALT_LENGTH];
|
|
||||||
// new SecureRandom().nextBytes(salt);
|
|
||||||
// return salt;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // --- Generate random IV ---
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
// public static byte[] generateIV() {
|
|
||||||
// byte[] iv = new byte[IV_LENGTH];
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
// new SecureRandom().nextBytes(iv);
|
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
|
||||||
// return iv;
|
|
||||||
// }
|
return cipher.doFinal(encryptedData);
|
||||||
//
|
}
|
||||||
// // --- Optional: Utility to base64 encode data ---
|
|
||||||
// public static String encodeBase64(byte[] data) {
|
// --- Generate random salt ---
|
||||||
// return Base64.getEncoder().encodeToString(data);
|
public static byte[] generateSalt() {
|
||||||
// }
|
byte[] salt = new byte[SALT_LENGTH];
|
||||||
//
|
new SecureRandom().nextBytes(salt);
|
||||||
// public static byte[] decodeBase64(String base64) {
|
return salt;
|
||||||
// return Base64.getDecoder().decode(base64);
|
}
|
||||||
// }
|
|
||||||
|
// --- Generate random IV ---
|
||||||
|
public static byte[] generateIV() {
|
||||||
|
byte[] iv = new byte[IV_LENGTH];
|
||||||
|
new SecureRandom().nextBytes(iv);
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Optional: Utility to base64 encode data ---
|
||||||
|
public static String encodeBase64(byte[] data) {
|
||||||
|
return Base64.getEncoder().encodeToString(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decodeBase64(String base64) {
|
||||||
|
return Base64.getDecoder().decode(base64);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,9 @@ import org.apache.hadoop.fs.FileSystem;
|
|||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -24,15 +26,18 @@ import java.security.PublicKey;
|
|||||||
public class FileService {
|
public class FileService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(FileService.class);
|
private static final Logger log = LoggerFactory.getLogger(FileService.class);
|
||||||
|
private final AuthenticationService authenticationService;
|
||||||
private final FileMetadataRepository fileMetadataRepository;
|
private final FileMetadataRepository fileMetadataRepository;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
public FileService(FileMetadataRepository fileMetadataRepository, UserRepository userRepository) {
|
@Autowired
|
||||||
|
public FileService(FileMetadataRepository fileMetadataRepository, UserRepository userRepository, AuthenticationService authenticationService) {
|
||||||
this.fileMetadataRepository = fileMetadataRepository;
|
this.fileMetadataRepository = fileMetadataRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
|
this.authenticationService = authenticationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
public void uploadEncryptedFile(String username, byte[] fileContent, String filename) throws Exception {
|
public void uploadEncryptedFile(String username, byte[] fileContent, String filename) throws Exception {
|
||||||
log.info("Starting upload for user={}, file={}", username, filename);
|
log.info("Starting upload for user={}, file={}", username, filename);
|
||||||
try {
|
try {
|
||||||
@@ -91,8 +96,8 @@ public class FileService {
|
|||||||
FileMetadata metadata = fileMetadataRepository.findByUsernameAndFilePath(username, filePath.toString())
|
FileMetadata metadata = fileMetadataRepository.findByUsernameAndFilePath(username, filePath.toString())
|
||||||
.orElseThrow(() -> new RuntimeException("File metadata not found for: " + filePath));
|
.orElseThrow(() -> new RuntimeException("File metadata not found for: " + filePath));
|
||||||
|
|
||||||
SecretKey derivedKey = EncryptionUtil.deriveKey(password.toCharArray(), user.getPrivateKeySalt());
|
// Use the cached decrypted private key
|
||||||
byte[] decryptedPrivateKeyBytes = EncryptionUtil.decrypt(user.getPrivateKey(), derivedKey, user.getPrivateKeyIv());
|
byte[] decryptedPrivateKeyBytes = authenticationService.getDecryptedPrivateKey(String.valueOf(user.getId()), password);
|
||||||
PrivateKey privateKey = RSAKeyUtil.decodePrivateKey(decryptedPrivateKeyBytes);
|
PrivateKey privateKey = RSAKeyUtil.decodePrivateKey(decryptedPrivateKeyBytes);
|
||||||
|
|
||||||
byte[] aesKeyBytes = EncryptionUtil.decryptRSA(metadata.getEncryptedKey(), privateKey);
|
byte[] aesKeyBytes = EncryptionUtil.decryptRSA(metadata.getEncryptedKey(), privateKey);
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.skycrate.backend.skycrateBackend.services;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class KeyCacheService {
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Long, String> keyCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void cacheKey(Long userId, String decryptedKey) {
|
||||||
|
keyCache.put(userId, decryptedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey(Long userId) {
|
||||||
|
return keyCache.get(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearKey(Long userId) {
|
||||||
|
keyCache.remove(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAllKeys() {
|
||||||
|
keyCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+16
-1
@@ -16,7 +16,7 @@ public class RefreshTokenService {
|
|||||||
|
|
||||||
private final RefreshTokenRepository refreshTokenRepo;
|
private final RefreshTokenRepository refreshTokenRepo;
|
||||||
|
|
||||||
@Value("${security.jwt.refresh-expiry-ms:604800000}") // 7 days default
|
@Value("${security.jwt.refresh-expiry-ms:86400000}") //1 day in milliseconds
|
||||||
private Long refreshTokenDurationMs;
|
private Long refreshTokenDurationMs;
|
||||||
|
|
||||||
public RefreshTokenService(RefreshTokenRepository refreshTokenRepo) {
|
public RefreshTokenService(RefreshTokenRepository refreshTokenRepo) {
|
||||||
@@ -35,6 +35,7 @@ public class RefreshTokenService {
|
|||||||
return refreshTokenRepo.save(token);
|
return refreshTokenRepo.save(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Optional<RefreshToken> findByToken(String token) {
|
public Optional<RefreshToken> findByToken(String token) {
|
||||||
return refreshTokenRepo.findByToken(token);
|
return refreshTokenRepo.findByToken(token);
|
||||||
}
|
}
|
||||||
@@ -45,6 +46,20 @@ public class RefreshTokenService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteByUser(User user) {
|
public void deleteByUser(User user) {
|
||||||
|
try {
|
||||||
refreshTokenRepo.deleteByUser(user);
|
refreshTokenRepo.deleteByUser(user);
|
||||||
|
System.out.println("Successfully deleted refresh tokens for user: " + user.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error deleting refresh tokens for user: " + user.getId() + " - " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void logout(User user) {
|
||||||
|
deleteByUser(user); // This should call the repository method to delete the token
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<RefreshToken> refreshAccessToken(String refreshToken) {
|
||||||
|
return findByToken(refreshToken).filter(token -> !isExpired(token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,13 +5,13 @@ spring.servlet.multipart.max-request-size=1000MB
|
|||||||
|
|
||||||
spring.servlet.multipart.enabled=true
|
spring.servlet.multipart.enabled=true
|
||||||
|
|
||||||
security.jwt.secret-key=PPp27xSTfBwOpRn4/AV6gPzQSnQg+Oi80KdWfCcuAHs=
|
security.jwt.secret-key=${JWT_SECRET}
|
||||||
security.jwt.expiration-time=3600000
|
security.jwt.expiration-time=3600000
|
||||||
|
|
||||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
|
||||||
spring.datasource.username=skycrateDB
|
spring.datasource.username=${DB_USERNAME}
|
||||||
spring.datasource.password=loa_dngLLA8729
|
spring.datasource.password=${DB_PASSWORD}
|
||||||
spring.datasource.url=jdbc:mysql://192.168.29.36:3306/skycrate
|
spring.datasource.url=jdbc:mysql://${DB_URI}/${DB_NAME}
|
||||||
|
|
||||||
spring.jpa.hibernate.ddl-auto=update
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
spring.jpa.show-sql=true
|
spring.jpa.show-sql=true
|
||||||
@@ -23,15 +23,14 @@ server.port=8080
|
|||||||
|
|
||||||
server.ssl.enabled=true
|
server.ssl.enabled=true
|
||||||
server.ssl.key-store=classpath:keystore.p12
|
server.ssl.key-store=classpath:keystore.p12
|
||||||
server.ssl.key-store-password=changeit
|
server.ssl.key-store-password=${SSL_PASSWORD}
|
||||||
server.ssl.key-store-type=PKCS12
|
server.ssl.key-store-type=PKCS12
|
||||||
server.ssl.key-alias=mykey
|
server.ssl.key-alias=skycrateSSL
|
||||||
|
|
||||||
management.endpoints.web.exposure.include=*
|
management.endpoints.web.exposure.include=*
|
||||||
management.endpoint.health.show-details=always
|
management.endpoint.health.show-details=always
|
||||||
management.endpoints.enabled-by-default=true
|
management.endpoints.enabled-by-default=true
|
||||||
|
|
||||||
|
|
||||||
# Allow unauthenticated access
|
# Allow unauthenticated access
|
||||||
#management.server.port=8080
|
#management.server.port=8080
|
||||||
#management.server.ssl.enabled=false
|
#management.server.ssl.enabled=false
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user