Refactor Auth and HDFS controllers, fix User model, and improve HDFS config
- Rewrote AuthController to inject all dependencies via constructor - Fixed token refresh/login logic and added rate limiter and blacklist support - Implemented getters in LoginRequest DTO - Updated User model to implement UserDetails and extend entity.User - Switched HDFScontroller to use entity.User instead of models.User - Rewrote HDFSConfig to include static getHDFS() method and secure config via env vars - Simplified JwtService, added overload for entity.User, and fixed key handling
This commit is contained in:
@@ -8,13 +8,35 @@ 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
|
||||
public class HDFSConfig {
|
||||
public static FileSystem getHDFS() throws Exception {
|
||||
Configuration conf = new Configuration();
|
||||
conf.set("fs.defaultFS", "hdfs://namenode:9000");
|
||||
return FileSystem.get(new URI("hdfs://namenode:9000"), conf);
|
||||
|
||||
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)
|
||||
|
||||
// Configures and returns a secured HDFS FileSystem instance.
|
||||
@Bean
|
||||
public FileSystem fileSystem() throws Exception {
|
||||
return getHDFS(); // use the static method internally
|
||||
}
|
||||
|
||||
// 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.");
|
||||
}
|
||||
|
||||
Configuration conf = new Configuration();
|
||||
conf.set("fs.defaultFS", HDFS_URI);
|
||||
|
||||
if (HDFS_USER != null && !HDFS_USER.isBlank()) {
|
||||
return UserGroupInformation.createRemoteUser(HDFS_USER)
|
||||
.doAs((PrivilegedExceptionAction<FileSystem>) () ->
|
||||
FileSystem.get(new URI(HDFS_URI), conf)
|
||||
);
|
||||
} else {
|
||||
return FileSystem.get(new URI(HDFS_URI), conf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package com.skycrate.backend.skycrateBackend.controller;
|
||||
|
||||
import com.skycrate.backend.skycrateBackend.dto.LoginRequest;
|
||||
import com.skycrate.backend.skycrateBackend.services.JwtService;
|
||||
import com.skycrate.backend.skycrateBackend.dto.LoginResponse;
|
||||
import com.skycrate.backend.skycrateBackend.dto.TokenRefreshRequest;
|
||||
import com.skycrate.backend.skycrateBackend.dto.TokenRefreshResponse;
|
||||
import com.skycrate.backend.skycrateBackend.entity.RefreshToken;
|
||||
import com.skycrate.backend.skycrateBackend.entity.User;
|
||||
import com.skycrate.backend.skycrateBackend.repository.UserRepository;
|
||||
import com.skycrate.backend.skycrateBackend.security.TokenBlacklistService;
|
||||
import com.skycrate.backend.skycrateBackend.services.JwtService;
|
||||
import com.skycrate.backend.skycrateBackend.services.RefreshTokenService;
|
||||
import com.skycrate.backend.skycrateBackend.services.RateLimiterService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
@@ -20,16 +24,26 @@ public class AuthController {
|
||||
private final AuthenticationManager authManager;
|
||||
private final JwtService jwtService;
|
||||
private final UserRepository userRepository;
|
||||
private final RefreshTokenService refreshTokenService;
|
||||
private final TokenBlacklistService tokenBlacklistService;
|
||||
private final RateLimiterService rateLimiterService;
|
||||
|
||||
public AuthController(AuthenticationManager authManager, JwtService jwtService, UserRepository userRepository) {
|
||||
public AuthController(
|
||||
AuthenticationManager authManager,
|
||||
JwtService jwtService,
|
||||
UserRepository userRepository,
|
||||
RefreshTokenService refreshTokenService,
|
||||
TokenBlacklistService tokenBlacklistService,
|
||||
RateLimiterService rateLimiterService
|
||||
) {
|
||||
this.authManager = authManager;
|
||||
this.jwtService = jwtService;
|
||||
this.userRepository = userRepository;
|
||||
this.refreshTokenService = refreshTokenService;
|
||||
this.tokenBlacklistService = tokenBlacklistService;
|
||||
this.rateLimiterService = rateLimiterService;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RefreshTokenService refreshTokenService;
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<?> login(@RequestBody LoginRequest request, HttpServletRequest servletRequest) {
|
||||
String ip = servletRequest.getRemoteAddr();
|
||||
@@ -52,43 +66,29 @@ public class AuthController {
|
||||
|
||||
rateLimiterService.resetAttempts(ip);
|
||||
|
||||
// ✅ Generate tokens
|
||||
String accessToken = jwtService.generateToken(user);
|
||||
RefreshToken refreshToken = refreshTokenService.createRefreshToken(user);
|
||||
|
||||
return ResponseEntity.ok(new LoginResponse(accessToken, refreshToken.getToken()));
|
||||
}
|
||||
|
||||
User user = userRepository.findByEmail(request.getEmail())
|
||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<?> logout(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
return ResponseEntity.badRequest().body("Missing or invalid Authorization header");
|
||||
}
|
||||
|
||||
rateLimiterService.resetAttempts(ip);
|
||||
String token = jwtService.generateToken(user);
|
||||
return ResponseEntity.ok().body(token);
|
||||
String token = authHeader.substring(7);
|
||||
|
||||
tokenBlacklistService.blacklistToken(token);
|
||||
|
||||
String email = jwtService.extractUsername(token);
|
||||
userRepository.findByEmail(email).ifPresent(refreshTokenService::deleteByUser);
|
||||
|
||||
return ResponseEntity.ok("Logged out successfully");
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private TokenBlacklistService tokenBlacklistService;
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<?> logout(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
return ResponseEntity.badRequest().body("Missing or invalid Authorization header");
|
||||
}
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
|
||||
// Blacklist access token
|
||||
tokenBlacklistService.blacklistToken(token);
|
||||
|
||||
// Extract user from token and delete their refresh token
|
||||
String email = jwtService.extractUsername(token);
|
||||
userRepository.findByEmail(email).ifPresent(refreshTokenService::deleteByUser);
|
||||
|
||||
return ResponseEntity.ok("Logged out successfully");
|
||||
}
|
||||
|
||||
@PostMapping("/refresh")
|
||||
public ResponseEntity<?> refresh(@RequestBody TokenRefreshRequest request) {
|
||||
String requestToken = request.getRefreshToken();
|
||||
@@ -105,5 +105,4 @@ public ResponseEntity<?> logout(HttpServletRequest request) {
|
||||
})
|
||||
.orElseGet(() -> ResponseEntity.status(403).body("Invalid refresh token"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,8 @@ package com.skycrate.backend.skycrateBackend.controller;
|
||||
|
||||
import com.skycrate.backend.skycrateBackend.config.HDFSConfig;
|
||||
import com.skycrate.backend.skycrateBackend.dto.ResponseDTO;
|
||||
import com.skycrate.backend.skycrateBackend.models.User;
|
||||
import com.skycrate.backend.skycrateBackend.entity.User;
|
||||
import com.skycrate.backend.skycrateBackend.repository.UserRepository;
|
||||
import com.skycrate.backend.skycrateBackend.services.EncryptionUtil;
|
||||
import com.skycrate.backend.skycrateBackend.services.HDFSOperations;
|
||||
import com.skycrate.backend.skycrateBackend.utils.KeyUtil;
|
||||
import com.skycrate.backend.skycrateBackend.utils.RSAKeyUtil;
|
||||
@@ -36,9 +35,6 @@ import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.File; // For java.io.File
|
||||
|
||||
import static com.skycrate.backend.skycrateBackend.utils.KeyUtil.getPrivateKeyForUser;
|
||||
import static com.skycrate.backend.skycrateBackend.utils.KeyUtil.getPublicKeyForUser;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/hdfs")
|
||||
public class HDFScontroller {
|
||||
|
||||
@@ -5,4 +5,9 @@ public class LoginRequest {
|
||||
private String password;
|
||||
|
||||
// Getters and setters
|
||||
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; }
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.skycrate.backend.skycrateBackend.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class User implements UserDetails {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private String email;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String password;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private String username;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String fullname;
|
||||
|
||||
@Lob
|
||||
private byte[] publicKey;
|
||||
|
||||
@Lob
|
||||
private byte[] privateKey;
|
||||
|
||||
// --- UserDetails interface methods ---
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return List.of(); // Add roles/authorities if needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return email; // or return username if that's your login key
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.skycrate.backend.skycrateBackend.models;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -13,7 +12,7 @@ import jakarta.persistence.*;
|
||||
|
||||
@Table(name = "users")
|
||||
@Entity
|
||||
public class User implements UserDetails {
|
||||
public class User extends com.skycrate.backend.skycrateBackend.entity.User implements UserDetails {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
@@ -1,83 +1,66 @@
|
||||
package com.skycrate.backend.skycrateBackend.services;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.skycrate.backend.skycrateBackend.entity.User;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Service
|
||||
public class JwtService {
|
||||
@Value("${security.jwt.secret-key}")
|
||||
private String secretKey;
|
||||
|
||||
@Value("${security.jwt.expiration-time}")
|
||||
private long jwtExpiration;
|
||||
// Recommend moving this key to environment variable or config file
|
||||
private static final String SECRET_KEY = "your-256-bit-secret-your-256-bit-secret"; // must be 256-bit
|
||||
|
||||
public String extractUsername(String token){
|
||||
return extractClaim(token,Claims::getSubject);
|
||||
private final long EXPIRATION_TIME = 1000 * 60 * 15; // 15 minutes
|
||||
|
||||
private Key getSigningKey() {
|
||||
return Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
|
||||
}
|
||||
|
||||
public <T> T extractClaim(String token,Function<Claims,T> claimsResolver){
|
||||
final Claims claims=extractAllClaims(token);
|
||||
public String extractUsername(String token) {
|
||||
return extractClaim(token, Claims::getSubject);
|
||||
}
|
||||
|
||||
public Date extractExpiration(String token) {
|
||||
return extractClaim(token, Claims::getExpiration);
|
||||
}
|
||||
|
||||
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return claimsResolver.apply(claims);
|
||||
|
||||
}
|
||||
|
||||
public String generateToken(UserDetails userDetails) {
|
||||
return generateToken(new HashMap<>(), userDetails);
|
||||
}
|
||||
|
||||
public String generateToken(Map<String, Object> extraClaims, UserDetails userDetails) {
|
||||
return buildToken(extraClaims, userDetails, jwtExpiration);
|
||||
}
|
||||
|
||||
public long getExpirtationTime(){
|
||||
return jwtExpiration;
|
||||
}
|
||||
private String buildToken(Map<String,Object> extraClaims,UserDetails userDetails,long expiration){
|
||||
|
||||
return Jwts.builder().setClaims(extraClaims).setSubject(userDetails.getUsername())
|
||||
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + expiration))
|
||||
.signWith(getSignInKey(), SignatureAlgorithm.HS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public boolean isTokenValid(String token, UserDetails userDetails) {
|
||||
final String username = extractUsername(token);
|
||||
return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
|
||||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||
}
|
||||
|
||||
private boolean isTokenExpired(String token) {
|
||||
public boolean isTokenExpired(String token) {
|
||||
return extractExpiration(token).before(new Date());
|
||||
}
|
||||
|
||||
private Date extractExpiration(String token) {
|
||||
return extractClaim(token, Claims::getExpiration);
|
||||
public String generateToken(UserDetails userDetails) {
|
||||
return Jwts.builder()
|
||||
.setSubject(userDetails.getUsername())
|
||||
.setIssuedAt(new Date())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
|
||||
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
private Claims extractAllClaims(String token) {
|
||||
return Jwts
|
||||
.parserBuilder()
|
||||
.setSigningKey(getSignInKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
private Key getSignInKey() {
|
||||
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
|
||||
return Keys.hmacShaKeyFor(keyBytes);
|
||||
// Overload for your entity
|
||||
public String generateToken(User user) {
|
||||
return generateToken((UserDetails) user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// NEED TO IMPLEMENT SAHI SE
|
||||
package com.skycrate.backend.skycrateBackend.services;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Service
|
||||
public class RateLimiterService {
|
||||
private final ConcurrentHashMap<String, Integer> attempts = new ConcurrentHashMap<>();
|
||||
|
||||
public boolean isBlocked(String ip) {
|
||||
return attempts.getOrDefault(ip, 0) >= 5;
|
||||
}
|
||||
|
||||
public void recordFailedAttempt(String ip) {
|
||||
attempts.put(ip, attempts.getOrDefault(ip, 0) + 1);
|
||||
}
|
||||
|
||||
public void resetAttempts(String ip) {
|
||||
attempts.remove(ip);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user