Skip to content

Commit

Permalink
Merge pull request #44 from jeffreybakker/feature/extension_2
Browse files Browse the repository at this point in the history
Feature/extension 2
  • Loading branch information
jeffreybakker authored Aug 3, 2017
2 parents 3fa9f12 + 95b14b0 commit 1c23dbd
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 19 deletions.
2 changes: 1 addition & 1 deletion src/main/java/honours/ing/banq/auth/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ public interface AuthService {
void deleteForCustomer(Customer customer);

Customer getAuthorizedCustomer(String token) throws NotAuthorizedError;
BankAccount getAuthorizedAccount(String iBAN, String pinCard, String pinCode) throws InvalidPINError;
BankAccount getAuthorizedAccount(String iBAN, String pinCard, String pinCode) throws InvalidPINError, CardBlockedError;

}
12 changes: 11 additions & 1 deletion src/main/java/honours/ing/banq/auth/AuthServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public Customer getAuthorizedCustomer(String token) throws NotAuthorizedError {
}

@Override
public BankAccount getAuthorizedAccount(String iBAN, String pinCard, String pinCode) throws InvalidPINError {
public BankAccount getAuthorizedAccount(String iBAN, String pinCard, String pinCode) throws InvalidPINError, CardBlockedError {
if (iBAN == null || iBAN.length() <= 8 || pinCard == null || pinCode == null) {
throw new InvalidParamValueError("One of the parameters is null or the IBAN is not long enough");
}
Expand All @@ -106,8 +106,18 @@ public BankAccount getAuthorizedAccount(String iBAN, String pinCard, String pinC
throw new InvalidParamValueError("Card does not exist");
}

if (card.isBlocked()) {
throw new CardBlockedError(
"There have been " + card.getFailedAttempts() + " consecutive failed attempts.");
}

if (!card.getPin().equals(pinCode)) {
card.addFailedAttempt();
cardRepository.save(card);
throw new InvalidPINError();
} else if (card.getFailedAttempts() > 0) {
card.resetAttempts();
cardRepository.save(card);
}

return account;
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/honours/ing/banq/auth/CardBlockedError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package honours.ing.banq.auth;

/**
* @author jeffrey
* @since 3-8-17
*/
public class CardBlockedError extends Exception {

public CardBlockedError() {
super();
}

public CardBlockedError(String s) {
super(s);
}

public CardBlockedError(String s, Throwable throwable) {
super(s, throwable);
}

public CardBlockedError(Throwable throwable) {
super(throwable);
}

protected CardBlockedError(String s, Throwable throwable, boolean b, boolean b1) {
super(s, throwable, b, b1);
}
}
22 changes: 22 additions & 0 deletions src/main/java/honours/ing/banq/card/Card.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
@Entity
public class Card {

private static final int PIN_ATTEMPTS_BEFORE_BLOCK = 3;

@SuppressWarnings("NumericOverflow")
private static final long DURABILITY = 1000 * 60 * 60 * 24 * 365 * 5; // 5 years

Expand All @@ -33,6 +35,8 @@ public class Card {
private String pin; // TODO: add hashing
private Date expirationDate;

private int failedAttempts;

/**
* @deprecated empty constructor for spring
*/
Expand All @@ -48,6 +52,8 @@ public Card(Customer holder, BankAccount account, String cardNumber) {
Calendar expiration = Calendar.getInstance();
expiration.setTimeInMillis(System.currentTimeMillis() + DURABILITY);
expirationDate = expiration.getTime();

failedAttempts = 0;
}

public Integer getId() {
Expand All @@ -74,6 +80,22 @@ public Date getExpirationDate() {
return expirationDate;
}

public int getFailedAttempts() {
return failedAttempts;
}

public boolean isBlocked() {
return failedAttempts >= PIN_ATTEMPTS_BEFORE_BLOCK;
}

public void addFailedAttempt() {
failedAttempts++;
}

public void resetAttempts() {
failedAttempts = 0;
}

private static String generatePin() {
StringBuilder res = new StringBuilder();
Random rnd = new Random();
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/honours/ing/banq/card/CardService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package honours.ing.banq.card;

import com.googlecode.jsonrpc4j.JsonRpcParam;
import com.googlecode.jsonrpc4j.JsonRpcService;
import honours.ing.banq.InvalidParamValueError;
import honours.ing.banq.access.NoEffectError;
import honours.ing.banq.auth.NotAuthorizedError;

/**
* @author jeffrey
* @since 3-8-17
*/
@JsonRpcService("/api/card")
public interface CardService {

/**
* A PIN card that has been blocked can be unblocked if the user logs in and calls this unblock card method.
* @param authToken the authentication token, obtained with {@code getAuthToken()}
* @param iBAN the number of the bank account
* @param pinCard the number of the pin card
* @return an empty dictionary if successful
* @throws InvalidParamValueError one or more parameter has an invalid value. See the message
* @throws NotAuthorizedError the authenticated user is not authorized to perform this action
* @throws NoEffectError if the card is not blocked this method will have no effect
*/
Object unblockCard(@JsonRpcParam("authToken") String authToken, @JsonRpcParam("iBAN") String iBAN,
@JsonRpcParam("pinCard") String pinCard)
throws InvalidParamValueError, NotAuthorizedError, NoEffectError;

}
67 changes: 67 additions & 0 deletions src/main/java/honours/ing/banq/card/CardServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package honours.ing.banq.card;

import com.googlecode.jsonrpc4j.spring.AutoJsonRpcServiceImpl;
import honours.ing.banq.InvalidParamValueError;
import honours.ing.banq.access.NoEffectError;
import honours.ing.banq.account.BankAccount;
import honours.ing.banq.account.BankAccountRepository;
import honours.ing.banq.auth.AuthService;
import honours.ing.banq.auth.NotAuthorizedError;
import honours.ing.banq.customer.Customer;
import honours.ing.banq.util.IBANUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@AutoJsonRpcServiceImpl
@Transactional(readOnly = true)
public class CardServiceImpl implements CardService {

// Services
@Autowired
private AuthService auth;

// Repositories
@Autowired
private CardRepository repository;

@Autowired
private BankAccountRepository accountRepository;

@Transactional
@Override
public Object unblockCard(String authToken, String iBAN, String pinCard)
throws InvalidParamValueError, NotAuthorizedError, NoEffectError {
Customer customer = auth.getAuthorizedCustomer(authToken);

// Retrieve the bank account and check whether we are authorized to access it
BankAccount account = accountRepository.findOne((int) IBANUtil.getAccountNumber(iBAN));
if (account == null) {
throw new InvalidParamValueError("There is no bank account with the given iBAN");
}

if (account.getPrimaryHolder() != customer && !account.getHolders().contains(customer)) {
throw new NotAuthorizedError();
}

// Retrieve the pin card and check whether we are authorized to access it
Card card = repository.findByAccountAndCardNumber(account, pinCard);
if (card == null) {
throw new InvalidParamValueError("There is no pin card with the given card number");
}

if (card.getHolder() != customer) {
throw new NotAuthorizedError();
}

if (!card.isBlocked()) {
throw new NoEffectError();
}

card.resetAttempts();
repository.save(card);

return new Object();
}
}
10 changes: 6 additions & 4 deletions src/main/java/honours/ing/banq/redirect/RedirectService.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,13 @@ public String redirect(@RequestBody String json) {
try {
Object service = context.getBean(serviceClass);

JsonRpcService annotation = AnnotationUtils.findAnnotation(service.getClass(),
JsonRpcService.class);
JsonRpcService annotation = AnnotationUtils.findAnnotation(
service.getClass(), JsonRpcService.class);

HttpPost httpPost = new HttpPost(DEFAULT_URL + annotation.value());
StringEntity msg = new StringEntity(json, ContentType.create("application/json",
"UTF-8"));
StringEntity msg = new StringEntity(
json, ContentType.create("application/json", "UTF-8"));

httpPost.setEntity(msg);
HttpResponse response = httpclient.execute(httpPost);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.googlecode.jsonrpc4j.JsonRpcService;
import honours.ing.banq.InvalidParamValueError;
import honours.ing.banq.account.BankAccount;
import honours.ing.banq.auth.CardBlockedError;
import honours.ing.banq.auth.InvalidPINError;
import honours.ing.banq.auth.NotAuthorizedError;
import honours.ing.banq.card.Card;
Expand All @@ -30,7 +31,7 @@ void depositIntoAccount(@JsonRpcParam("iBAN") String iBAN, @JsonRpcParam("pinCar
pinCard,
@JsonRpcParam("pinCode") String pinCode, @JsonRpcParam("amount")
Double amount)
throws InvalidParamValueError, InvalidPINError;
throws InvalidParamValueError, InvalidPINError, CardBlockedError;

/**
* Transfer money to a {@link BankAccount} using a {@link Card}
Expand All @@ -46,7 +47,7 @@ void depositIntoAccount(@JsonRpcParam("iBAN") String iBAN, @JsonRpcParam("pinCar
void payFromAccount(@JsonRpcParam("sourceIBAN") String sourceIBAN, @JsonRpcParam
("targetIBAN") String targetIBAN, @JsonRpcParam("pinCard") String pinCard,
@JsonRpcParam("pinCode") String pinCode, @JsonRpcParam("amount") Double
amount) throws InvalidParamValueError, InvalidPINError;
amount) throws InvalidParamValueError, InvalidPINError, CardBlockedError;

/**
* Transer money from one {@link BankAccount} to another {@link BankAccount}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import honours.ing.banq.account.BankAccount;
import honours.ing.banq.account.BankAccountRepository;
import honours.ing.banq.auth.AuthService;
import honours.ing.banq.auth.CardBlockedError;
import honours.ing.banq.auth.InvalidPINError;
import honours.ing.banq.auth.NotAuthorizedError;
import honours.ing.banq.card.Card;
Expand Down Expand Up @@ -45,7 +46,7 @@ public class TransactionServiceImpl implements TransactionService {

@Override
public void depositIntoAccount(String iBAN, String pinCard, String pinCode, Double amount)
throws InvalidParamValueError, InvalidPINError {
throws InvalidParamValueError, InvalidPINError, CardBlockedError {
if (!IBANUtil.isValidIBAN(iBAN)) {
throw new InvalidParamValueError("The given IBAN is not valid.");
}
Expand All @@ -69,7 +70,7 @@ public void depositIntoAccount(String iBAN, String pinCard, String pinCode, Doub

@Override
public void payFromAccount(String sourceIBAN, String targetIBAN, String pinCard, String pinCode, Double amount)
throws InvalidParamValueError, InvalidPINError {
throws InvalidParamValueError, InvalidPINError, CardBlockedError {
if (!IBANUtil.isValidIBAN(sourceIBAN)) {
throw new InvalidParamValueError("The given source IBAN is not valid.");
}
Expand Down
22 changes: 13 additions & 9 deletions src/test/java/honours/ing/banq/BoilerplateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,23 @@ public class BoilerplateTest {

@Before
public void setUp() throws Exception {
account1 = new AccountInfo(bankAccountService.openAccount("Jan", "Jansen", "J.", "1996-1-1",
"1234567890", "Klaverstraat 1", "0612345678", "janjansen@gmail.com", "jantje96",
"1234"), "jantje96", "1234");
account2 = new AccountInfo(bankAccountService.openAccount("Piet", "Pietersen", "p.p", "1998-8-8",
"012345789", "Huisstraat 1", "0607080910", "piet@gmail.com", "piet1", "1234"), "piet1", "1234");
account1 = new AccountInfo(
bankAccountService.openAccount(
"Jan", "Jansen", "J.", "1996-1-1",
"1234567890", "Klaverstraat 1", "0612345678",
"janjansen@gmail.com", "jantje96", "1234"),
"jantje96", "1234");
account2 = new AccountInfo(
bankAccountService.openAccount("Piet", "Pietersen", "p.p", "1998-8-8",
"012345789", "Huisstraat 1", "0607080910",
"piet@gmail.com", "piet1", "1234"),
"piet1", "1234");

account1.token = authService.getAuthToken("jantje96", "1234").getAuthToken();
account2.token = authService.getAuthToken("piet1", "1234").getAuthToken();

assertThat(infoService.getBalance(account1.token, account1.iBan).getBalance(), equalTo
(0d));
assertThat(infoService.getBalance(account2.token, account2.iBan).getBalance(), equalTo
(0d));
assertThat(infoService.getBalance(account1.token, account1.iBan).getBalance(), equalTo(0d));
assertThat(infoService.getBalance(account2.token, account2.iBan).getBalance(), equalTo(0d));
}

@After
Expand Down
47 changes: 47 additions & 0 deletions src/test/java/honours/ing/banq/card/CardServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package honours.ing.banq.card;

import honours.ing.banq.BoilerplateTest;
import honours.ing.banq.auth.CardBlockedError;
import honours.ing.banq.auth.InvalidPINError;
import honours.ing.banq.transaction.TransactionService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import static org.junit.Assert.*;

/**
* @author jeffrey
* @since 3-8-17
*/
public class CardServiceTest extends BoilerplateTest {

@Autowired
private CardService service;

@Autowired
private TransactionService transactionService;

@Test(expected = CardBlockedError.class)
public void blockCard() throws Exception {
for (int i = 0; i < 3; i++) {
try {
transactionService.depositIntoAccount(account1.iBan, account1.cardNumber, "0000", 10.0);
} catch (InvalidPINError ignored) { }
}

transactionService.depositIntoAccount(account1.iBan, account1.cardNumber, account1.pin, 10.0);
}

@Test
public void unblockCard() throws Exception {
try {
blockCard();
} catch (CardBlockedError ignored) { }

service.unblockCard(account1.token, account1.iBan, account1.cardNumber);

// Now test if the card is usable again
transactionService.depositIntoAccount(account1.iBan, account1.cardNumber, account1.pin, 10.0);
}

}

0 comments on commit 1c23dbd

Please sign in to comment.