Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified mvnw
100644 → 100755
Empty file.
115 changes: 42 additions & 73 deletions src/main/java/com/example/ledgersystem/DataSeeder.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

@Component // <--- Tells Spring to manage this class
Expand All @@ -40,77 +40,46 @@ public void run(String... args) throws Exception {

log.info("Starting database seed...");

Role userRole = roleRepository
.findByRoleName(AppRoles.ROLE_USER)
.orElseGet(() -> roleRepository.save(new Role(AppRoles.ROLE_USER)));
log.debug("Role created/found: {}", AppRoles.ROLE_USER);

Role adminRole = roleRepository
.findByRoleName(AppRoles.ROLE_ADMIN)
.orElseGet(() -> roleRepository.save(new Role(AppRoles.ROLE_ADMIN)));
log.debug("Role created/found: {}", AppRoles.ROLE_ADMIN);

User systemAdmin = new User();
systemAdmin.setUsername("systemAdmin");
systemAdmin.setPassword(passwordEncoder.encode("pass123"));
systemAdmin.setRole(List.of(userRole, adminRole));
systemAdmin.setEmail("system@gmail.com");
userRepository.save(systemAdmin);
log.debug("System admin user created: username=systemAdmin");

Account systemAccount = new Account();
systemAccount.setName("CENTRAL_BANK");
systemAccount.setBalance(new BigDecimal("0.00"));
systemAccount.setCurrency("INR");
systemAccount.setUser(systemAdmin);
accountRepository.save(systemAccount);
log.debug("CENTRAL_BANK account created: accountId={}", systemAccount.getAccountId());

// ---- CREATE USERS ----
User aliceUser = new User();
aliceUser.setEmail("alice@gmail.com");
aliceUser.setPassword(passwordEncoder.encode("pass123"));
aliceUser.setUsername("alice");
aliceUser.setRole(List.of(userRole));

User bobUser = new User();
bobUser.setEmail("bob@gmail.com");
bobUser.setPassword(passwordEncoder.encode("pass123"));
bobUser.setUsername("bob");
bobUser.setRole(List.of(userRole, adminRole));

User charlieUser = new User();
charlieUser.setEmail("charlie@gmail.com");
charlieUser.setPassword(passwordEncoder.encode("pass123"));
charlieUser.setUsername("charlie");
charlieUser.setRole(List.of(userRole));

userRepository.saveAll(List.of(aliceUser, bobUser, charlieUser));
log.debug("Test users created: alice, bob, charlie");

// 2. Create Dummy Accounts
Account alice = new Account();
alice.setName("Alice");
alice.setCurrency("INR");
alice.setBalance(new BigDecimal("1000.0000")); // Initial "Gift"
alice.setUser(aliceUser);

Account bob = new Account();
bob.setName("Bob");
bob.setCurrency("INR");
bob.setBalance(new BigDecimal("1000.0000"));
bob.setUser(bobUser);

Account charlie = new Account();
charlie.setName("Charlie");
charlie.setCurrency("INR");
charlie.setBalance(new BigDecimal("5000.0000"));
charlie.setUser(charlieUser);

// 3. Save to DB
accountRepository.saveAll(Arrays.asList(alice, bob, charlie));

log.info("Database seeded successfully. Accounts: alice={}, bob={}, charlie={}",
alice.getAccountId(), bob.getAccountId(), charlie.getAccountId());
try {
Role userRole = roleRepository
.findByRoleName(AppRoles.ROLE_USER)
.orElseGet(() -> roleRepository.save(new Role(AppRoles.ROLE_USER)));
log.debug("Role created/found: {}", AppRoles.ROLE_USER);

Role adminRole = roleRepository
.findByRoleName(AppRoles.ROLE_ADMIN)
.orElseGet(() -> roleRepository.save(new Role(AppRoles.ROLE_ADMIN)));
log.debug("Role created/found: {}", AppRoles.ROLE_ADMIN);

User systemAdmin = new User();
systemAdmin.setUsername("systemAdmin");
systemAdmin.setPassword(passwordEncoder.encode("pass123"));
systemAdmin.setRole(List.of(userRole, adminRole));
systemAdmin.setEmail("system@admin.com");
userRepository.save(systemAdmin);
log.debug("System admin user created: username=systemAdmin, email=system@admin.com");

Account centralBank = new Account();
centralBank.setName("CENTRAL_BANK");
centralBank.setBalance(new BigDecimal("0.00"));
centralBank.setCurrency("INR");
centralBank.setUser(systemAdmin);
accountRepository.save(centralBank);
log.debug("CENTRAL_BANK account created: accountId={}", centralBank.getAccountId());

log.info("Database seeded successfully.");
log.info("=====================================");
log.info("System ready! Available endpoints:");
log.info(" POST /api/auth/signup - Register a new user");
log.info(" POST /api/auth/signin - Login");
log.info(" POST /api/account/create - Create a new account (auth required)");
log.info(" GET /api/account/list - List your accounts (auth required)");
log.info(" POST /api/deposit - Deposit funds (auth required)");
log.info(" POST /api/transfer - Transfer funds (auth required)");
log.info(" POST /api/withdraw - Withdraw funds (auth required)");
log.info("=====================================");
} catch (DataIntegrityViolationException e) {
log.warn("Data seeding skipped due to concurrent initialization: {}", e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.ledgersystem.Payloads;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CreateAccountDTO {

@NotBlank
@Size(min = 1, max = 50)
private String accountName;

@NotBlank
@Pattern(regexp = "^[A-Z]{3}$", message = "Currency must be a valid 3-letter ISO 4217 code (e.g. INR, USD)")
private String currency;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import jakarta.validation.constraints.Size;
import lombok.Data;

import java.util.Set;

@Data
public class SignUpRequest {
@Email
Expand All @@ -20,4 +22,6 @@ public class SignUpRequest {
@Size(min = 3, max = 30)
private String username;

private Set<String> role;

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

import com.example.ledgersystem.Exceptions.APIexception;
import com.example.ledgersystem.Payloads.ApiResponse;
import com.example.ledgersystem.Payloads.CreateAccountDTO;
import com.example.ledgersystem.Payloads.DepositRequestDTO;
import com.example.ledgersystem.Payloads.MoneyTransferDTO;
import com.example.ledgersystem.Payloads.StatementResponse;
import com.example.ledgersystem.Payloads.WithdrawRequestDTO;
import com.example.ledgersystem.config.AppConst;
import com.example.ledgersystem.enums.RateLimitType;
import com.example.ledgersystem.model.Account;
import com.example.ledgersystem.model.User;
import com.example.ledgersystem.repositories.AccountRepository;
import com.example.ledgersystem.repositories.UserRepository;
import com.example.ledgersystem.service.AccountService;
import com.example.ledgersystem.service.RateLimitingService;
import com.example.ledgersystem.utils.AuthUtils;
Expand All @@ -22,6 +25,7 @@
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

Expand All @@ -35,6 +39,8 @@ public class AccountController {
@Autowired
private AccountRepository accountRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private AuthUtils authUtils;
@Autowired
private RateLimitingService rateLimitingService;
Expand All @@ -49,6 +55,56 @@ private boolean isRateLimited(UUID userId, RateLimitType type) {
return !bucket.tryConsume(1);
}

@PostMapping("/account/create")
public ResponseEntity<ApiResponse> createAccount(@Valid @RequestBody CreateAccountDTO createAccountDTO) {
UUID userId = authUtils.loggedInUserId();
log.info("Create account API called: accountName={}, currency={}, user={}", createAccountDTO.getAccountName(), createAccountDTO.getCurrency(), userId);

// 🛡️ SHIELD
if (isRateLimited(userId, RateLimitType.GENERAL)) {
log.warn("Rate limit exceeded: userId={}, type=GENERAL, endpoint=/api/account/create", userId);
return new ResponseEntity<>(
new ApiResponse("Too many requests! Please wait a minute.", false),
HttpStatus.TOO_MANY_REQUESTS
);
}

User user = userRepository.findById(userId)
.orElseThrow(() -> new APIexception("User not found"));

Account account = new Account();
account.setName(createAccountDTO.getAccountName());
account.setCurrency(createAccountDTO.getCurrency());
account.setBalance(BigDecimal.ZERO);
account.setUser(user);
accountRepository.save(account);

log.info("Create account API completed: accountId={}, user={}", account.getAccountId(), userId);
return new ResponseEntity<>(new ApiResponse("Account created successfully!", true), HttpStatus.CREATED);
}

@GetMapping("/account/list")
public ResponseEntity<?> listAccounts() {
UUID userId = authUtils.loggedInUserId();
log.info("List accounts API called: user={}", userId);

// 🛡️ SHIELD
if (isRateLimited(userId, RateLimitType.GENERAL)) {
log.warn("Rate limit exceeded: userId={}, type=GENERAL, endpoint=/api/account/list", userId);
return new ResponseEntity<>(
new ApiResponse("Too many requests! Please wait a minute.", false),
HttpStatus.TOO_MANY_REQUESTS
);
}

User user = userRepository.findById(userId)
.orElseThrow(() -> new APIexception("User not found"));

List<Account> accounts = accountRepository.findAllByUser(user);
log.debug("List accounts API completed: user={}, count={}", userId, accounts.size());
return new ResponseEntity<>(accounts, HttpStatus.OK);
}

@PostMapping("/transfer")
public ResponseEntity<ApiResponse> transferMoney(@Valid @RequestBody MoneyTransferDTO moneyTransferDTO) {
UUID userId = authUtils.loggedInUserId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import com.example.ledgersystem.Security.Response.UserInfoResponse;
import com.example.ledgersystem.Security.Services.UserDetailsImpl;
import com.example.ledgersystem.Security.jwt.JwtUtils;
import com.example.ledgersystem.model.AppRoles;
import com.example.ledgersystem.model.Role;
import com.example.ledgersystem.model.User;
import com.example.ledgersystem.repositories.RoleRepository;
import com.example.ledgersystem.repositories.UserRepository;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -27,7 +30,9 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
Expand All @@ -44,6 +49,9 @@ public class AuthController {
@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Autowired
private PasswordEncoder passwordEncoder;

Expand Down Expand Up @@ -83,28 +91,33 @@ public ResponseEntity<?> authenticateUser(@RequestBody LoginRequestDTO loginRequ
public ResponseEntity<?> registerUser(@Valid @RequestBody SignUpRequest signUpRequest){
log.info("Sign-up attempt: username={}, email={}", signUpRequest.getUsername(), signUpRequest.getEmail());

//Checking for already existing account
//Checking for already existing account
if(userRepository.existsByUsername(signUpRequest.getUsername())){
log.warn("Sign-up rejected: username already taken, username={}", signUpRequest.getUsername());
// FIX: Send the object, not the string
return ResponseEntity.badRequest().body(new MessageResponse("Error: Username is already taken!"));
}

if(userRepository.existsByEmail(signUpRequest.getEmail())){
log.warn("Sign-up rejected: email already in use, email={}", signUpRequest.getEmail());
// FIX: Send the object, not the string
return ResponseEntity.badRequest().body(new MessageResponse("Error: Email is already in use!"));
}

// Resolve roles - only ROLE_USER is assignable during self-service signup
List<Role> roles = new ArrayList<>();
Role userRole = roleRepository.findByRoleName(AppRoles.ROLE_USER)
.orElseGet(() -> roleRepository.save(new Role(AppRoles.ROLE_USER)));
roles.add(userRole);
log.debug("Sign-up: assigned ROLE_USER to username={}", signUpRequest.getUsername());

//Saving User
User user = new User( //Creating user object for saving
User user = new User(
signUpRequest.getUsername(),
passwordEncoder.encode(signUpRequest.getPassword()),
signUpRequest.getEmail()
);
user.setRole(roles);
userRepository.save(user);
log.info("Sign-up successful: username={}, email={}", signUpRequest.getUsername(), signUpRequest.getEmail());
log.info("Sign-up successful: username={}, email={}, roles={}", signUpRequest.getUsername(), signUpRequest.getEmail(), roles);
return ResponseEntity.ok(new MessageResponse("User registered successfully!"));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.example.ledgersystem.repositories;

import com.example.ledgersystem.model.Account;
import com.example.ledgersystem.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Repository
public interface AccountRepository extends JpaRepository<Account, UUID> {

Optional<Account> findByName(String centralBank);

List<Account> findAllByUser(User user);
}
Loading