Merge commit 'd1339729ec3e31d7bb5159640909d9839aa6a45f' into wip-customer

* commit 'd1339729ec3e31d7bb5159640909d9839aa6a45f':
  added simultaneous token-based and http basic authentication
  added simultaneous token-based and http basic authentication
This commit is contained in:
Andrew Revinsky (DART)
2016-02-19 04:34:57 +03:00
10 changed files with 239 additions and 114 deletions

View File

@@ -3,6 +3,7 @@ package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.QuerySideCustomer;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.filter.StatelessAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@@ -52,14 +53,16 @@ public class AuthConfiguration extends WebSecurityConfigurerAdapter {
@Override
public UserDetailsService userDetailsServiceBean() {
return email -> {
QuerySideCustomer customer = customerAuthService.findByEmail(email);
/* QuerySideCustomer customer = customerAuthService.findByEmail(email);
if (customer != null) {
return new User(email, "", true, true, true, true,
AuthorityUtils.createAuthorityList("USER"));
return new User(email);
} else {
throw new UsernameNotFoundException(String.format("could not find the customer '%s'", email));
}
};
}*/
//authorize everyone with basic authentication
return new User(email, "", true, true, true, true,
AuthorityUtils.createAuthorityList("USER"));
};
}
@Bean

View File

@@ -20,8 +20,9 @@ public class CustomerAuthService {
List<QuerySideCustomer> customers = customerAuthRepository.findByEmail(email);
if (customers.isEmpty())
throw new EmptyResultDataAccessException(1);
else if(customers.size()>1)
throw new IncorrectResultSizeDataAccessException(1, customers.size());
//TODO: add unique email constraint
/* else if(customers.size()>1)
throw new IncorrectResultSizeDataAccessException(1, customers.size());*/
else
return customers.get(0);
}

View File

@@ -21,7 +21,7 @@ public class TokenAuthenticationService {
@Autowired
private TokenService tokenService;
private static final String AUTH_HEADER_NAME = "x-access-token";
private static final String AUTH_HEADER_NAME = "access-token";
private static final long DAY = 1000 * 60 * 60 * 24;
private ObjectMapper mapper = new ObjectMapper();

View File

@@ -24,7 +24,7 @@ public class StatelessAuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
if (SecurityContextHolder.getContext().getAuthentication()==null) {
SecurityContextHolder.getContext().setAuthentication(
tokenAuthenticationService.getAuthentication((HttpServletRequest) req));
}

View File

@@ -0,0 +1,48 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import rx.Observable;
import java.nio.charset.Charset;
/**
* Created by Main on 18.02.2016.
*/
public class BasicAuthUtils {
public static HttpHeaders basicAuthHeaders(String username) {
return new HttpHeaders() {
{
String auth = username + ":";
byte[] encodedAuth = Base64.encodeBase64(
auth.getBytes(Charset.forName("US-ASCII")));
String authHeader = "Basic " + new String(encodedAuth);
set("Authorization", authHeader);
}
};
}
public static <T> T doRestTemplateRequest(RestTemplate restTemplate, String url, HttpMethod httpMethod, Class<T> responseType) {
return doRestTemplateRequest(restTemplate, url, httpMethod, responseType, null);
}
public static <T> T doRestTemplateRequest(RestTemplate restTemplate, String url, HttpMethod httpMethod, Class<T> responseType, Object requestObject) {
HttpEntity httpEntity;
if(requestObject!=null) {
httpEntity = new HttpEntity(requestObject, BasicAuthUtils.basicAuthHeaders("test_user@mail.com"));
} else {
httpEntity = new HttpEntity(BasicAuthUtils.basicAuthHeaders("test_user@mail.com"));
}
return restTemplate.exchange(url,
httpMethod,
httpEntity,
responseType).getBody();
}
}

View File

@@ -1,5 +1,8 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
/**
* Created by popikyardo on 03.02.16.
*/
@@ -31,4 +34,14 @@ public class CustomerResponse {
public void setCustomerInfo(CustomerInfo customerInfo) {
this.customerInfo = customerInfo;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -8,6 +8,7 @@ dependencies {
testCompile project(":accounts-query-side-web")
testCompile project(":testutil")
testCompile project(":common-auth")
testCompile "junit:junit:4.11"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile scalaTestDependency

View File

@@ -5,6 +5,7 @@ import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Addr
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Name;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils.BasicAuthUtils;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CreateMoneyTransferRequest;
@@ -15,6 +16,7 @@ import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
@@ -81,10 +83,21 @@ public class EndToEndTest {
assertCustomerResponse(customerId, customerInfo);
final CreateAccountResponse fromAccount = restTemplate.postForEntity(accountsCommandSideBaseUrl("/accounts"), new CreateAccountRequest(customerId, "My #1 Account", initialFromAccountBalance), CreateAccountResponse.class).getBody();
final CreateAccountResponse fromAccount = BasicAuthUtils.doRestTemplateRequest(restTemplate,
accountsCommandSideBaseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest(customerId, "My #1 Account", initialFromAccountBalance)
);
final String fromAccountId = fromAccount.getAccountId();
CreateAccountResponse toAccount = restTemplate.postForEntity(accountsCommandSideBaseUrl("/accounts"), new CreateAccountRequest(customerId, "My #2 Account", initialToAccountBalance), CreateAccountResponse.class).getBody();
CreateAccountResponse toAccount = BasicAuthUtils.doRestTemplateRequest(restTemplate,
accountsCommandSideBaseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest(customerId, "My #2 Account", initialToAccountBalance)
);
String toAccountId = toAccount.getAccountId();
Assert.assertNotNull(fromAccountId);
@@ -94,8 +107,12 @@ public class EndToEndTest {
assertAccountBalance(toAccountId, initialToAccountBalance);
final CreateMoneyTransferResponse moneyTransfer = restTemplate.postForEntity(transactionsCommandSideBaseUrl("/transfers"),
new CreateMoneyTransferRequest(fromAccountId, toAccountId, amountToTransfer), CreateMoneyTransferResponse.class).getBody();
final CreateMoneyTransferResponse moneyTransfer = BasicAuthUtils.doRestTemplateRequest(restTemplate,
transactionsCommandSideBaseUrl("/transfers"),
HttpMethod.POST,
CreateMoneyTransferResponse.class,
new CreateMoneyTransferRequest(fromAccountId, toAccountId, amountToTransfer)
);
assertAccountBalance(fromAccountId, finalFromAccountBalance);
assertAccountBalance(toAccountId, finalToAccountBalance);
@@ -113,7 +130,10 @@ public class EndToEndTest {
new Producer<GetAccountResponse>() {
@Override
public Observable<GetAccountResponse> produce() {
return Observable.just(restTemplate.getForEntity(accountsQuerySideBaseUrl("/accounts/" + fromAccountId), GetAccountResponse.class).getBody());
return Observable.just(BasicAuthUtils.doRestTemplateRequest(restTemplate,
accountsQuerySideBaseUrl("/accounts/" + fromAccountId),
HttpMethod.GET,
GetAccountResponse.class));
}
},
new Verifier<GetAccountResponse>() {
@@ -130,7 +150,10 @@ public class EndToEndTest {
new Producer<CustomerResponse>() {
@Override
public Observable<CustomerResponse> produce() {
return Observable.just(restTemplate.getForEntity(customersQuerySideBaseUrl("/customers/" + customerId), CustomerResponse.class).getBody());
return Observable.just(BasicAuthUtils.doRestTemplateRequest(restTemplate,
customersQuerySideBaseUrl("/customers/" + customerId),
HttpMethod.GET,
CustomerResponse.class));
}
},
new Verifier<CustomerResponse>() {

View File

@@ -5,6 +5,7 @@ import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Cust
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Name;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.AuthRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils.BasicAuthUtils;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import org.junit.Assert;
@@ -14,6 +15,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;
@@ -42,7 +45,8 @@ public class BankingAuthTest {
@Test
public void shouldCreateCustomerAndLogin() {
CustomerInfo customerInfo = generateCustomerInfo();
String email = uniqueEmail();
CustomerInfo customerInfo = generateCustomerInfo(email);
final CustomerResponse customerResponse = restTemplate.postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
@@ -52,7 +56,7 @@ public class BankingAuthTest {
assertCustomerResponse(customerId, customerInfo);
AuthRequest authRequest = new AuthRequest("current@email.com");
AuthRequest authRequest = new AuthRequest(email);
final CustomerResponse loginCustomerResponse = restTemplate.postForEntity(baseUrl("/login"), authRequest, CustomerResponse.class).getBody();
@@ -64,7 +68,10 @@ public class BankingAuthTest {
new Producer<CustomerResponse>() {
@Override
public Observable<CustomerResponse> produce() {
return Observable.just(restTemplate.getForEntity(baseUrl("/customers/" + customerId), CustomerResponse.class).getBody());
return Observable.just(BasicAuthUtils.doRestTemplateRequest(restTemplate,
baseUrl("/customers/" + customerId),
HttpMethod.GET,
CustomerResponse.class));
}
},
new Verifier<CustomerResponse>() {
@@ -76,10 +83,10 @@ public class BankingAuthTest {
});
}
private CustomerInfo generateCustomerInfo() {
private CustomerInfo generateCustomerInfo(String email) {
return new CustomerInfo(
new Name("John", "Doe"),
"current@email.com",
email,
"000-00-0000",
"1-111-111-1111",
new Address("street 1",
@@ -89,4 +96,8 @@ public class BankingAuthTest {
"1111111")
);
}
private String uniqueEmail() {
return System.currentTimeMillis() + "@email.com";
}
}

View File

@@ -1,11 +1,14 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.*;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils.BasicAuthUtils;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CreateMoneyTransferRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CreateMoneyTransferResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.accounts.GetAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountResponse;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -13,16 +16,15 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;
import rx.Observable;
import java.math.BigDecimal;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import rx.Observable;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
@RunWith(SpringJUnit4ClassRunner.class)
@@ -31,114 +33,137 @@ import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil
@IntegrationTest({"server.port=0", "management.port=0"})
public class BankingWebIntegrationTest {
@Value("${local.server.port}")
private int port;
@Value("${local.server.port}")
private int port;
private String baseUrl(String path) {
return "http://localhost:" + port + "/" + path;
}
private String baseUrl(String path) {
return "http://localhost:" + port + "/" + path;
}
@Autowired
RestTemplate restTemplate;
@Autowired
RestTemplate restTemplate;
@Test
public void shouldCreateAccountsAndTransferMoney() {
BigDecimal initialFromAccountBalance = new BigDecimal(500);
BigDecimal initialToAccountBalance = new BigDecimal(100);
BigDecimal amountToTransfer = new BigDecimal(150);
@Test
public void shouldCreateAccountsAndTransferMoney() {
BigDecimal initialFromAccountBalance = new BigDecimal(500);
BigDecimal initialToAccountBalance = new BigDecimal(100);
BigDecimal amountToTransfer = new BigDecimal(150);
BigDecimal finalFromAccountBalance = initialFromAccountBalance.subtract(amountToTransfer);
BigDecimal finalToAccountBalance = initialToAccountBalance.add(amountToTransfer);
BigDecimal finalFromAccountBalance = initialFromAccountBalance.subtract(amountToTransfer);
BigDecimal finalToAccountBalance = initialToAccountBalance.add(amountToTransfer);
final CreateAccountResponse fromAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest("00000000-00000000", "My Account", initialFromAccountBalance), CreateAccountResponse.class).getBody();
final String fromAccountId = fromAccount.getAccountId();
final CreateAccountResponse fromAccount = BasicAuthUtils.doRestTemplateRequest(restTemplate,
baseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest("00000000-00000000", "My 1 Account", initialFromAccountBalance)
);
final String fromAccountId = fromAccount.getAccountId();
CreateAccountResponse toAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest("00000000-00000000", "My Account", initialToAccountBalance), CreateAccountResponse.class).getBody();
String toAccountId = toAccount.getAccountId();
CreateAccountResponse toAccount = BasicAuthUtils.doRestTemplateRequest(restTemplate,
baseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest("00000000-00000000", "My 2 Account", initialToAccountBalance)
);
Assert.assertNotNull(fromAccountId);
Assert.assertNotNull(toAccountId);
String toAccountId = toAccount.getAccountId();
assertAccountBalance(fromAccountId, initialFromAccountBalance);
assertAccountBalance(toAccountId, initialToAccountBalance);
Assert.assertNotNull(fromAccountId);
Assert.assertNotNull(toAccountId);
assertAccountBalance(fromAccountId, initialFromAccountBalance);
assertAccountBalance(toAccountId, initialToAccountBalance);
final CreateMoneyTransferResponse moneyTransfer = restTemplate.postForEntity(baseUrl("/transfers"),
new CreateMoneyTransferRequest(fromAccountId, toAccountId, amountToTransfer), CreateMoneyTransferResponse.class).getBody();
final CreateMoneyTransferResponse moneyTransfer = BasicAuthUtils.doRestTemplateRequest(restTemplate,
baseUrl("/transfers"),
HttpMethod.POST,
CreateMoneyTransferResponse.class,
new CreateMoneyTransferRequest(fromAccountId, toAccountId, amountToTransfer)
);
assertAccountBalance(fromAccountId, finalFromAccountBalance);
assertAccountBalance(toAccountId, finalToAccountBalance);
assertAccountBalance(fromAccountId, finalFromAccountBalance);
assertAccountBalance(toAccountId, finalToAccountBalance);
}
}
@Test
public void shouldCreateCustomers() {
CustomerInfo customerInfo = generateCustomerInfo();
@Test
public void shouldCreateCustomers() {
CustomerInfo customerInfo = generateCustomerInfo();
final CustomerResponse customerResponse = restTemplate.postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
final CustomerResponse customerResponse = restTemplate.postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
Assert.assertNotNull(customerId);
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
Assert.assertNotNull(customerId);
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
assertCustomerResponse(customerId, customerInfo);
}
assertCustomerResponse(customerId, customerInfo);
}
private BigDecimal toCents(BigDecimal dollarAmount) {
return dollarAmount.multiply(new BigDecimal(100));
}
private BigDecimal toCents(BigDecimal dollarAmount) {
return dollarAmount.multiply(new BigDecimal(100));
}
private void assertAccountBalance(final String fromAccountId, final BigDecimal expectedBalanceInDollars) {
final BigDecimal inCents = toCents(expectedBalanceInDollars);
eventually(
new Producer<GetAccountResponse>() {
@Override
public Observable<GetAccountResponse> produce() {
return Observable.just(restTemplate.getForEntity(baseUrl("/accounts/" + fromAccountId), GetAccountResponse.class).getBody());
}
},
new Verifier<GetAccountResponse>() {
@Override
public void verify(GetAccountResponse accountInfo) {
Assert.assertEquals(fromAccountId, accountInfo.getAccountId());
Assert.assertEquals(inCents, accountInfo.getBalance());
}
});
}
private void assertAccountBalance(final String fromAccountId, final BigDecimal expectedBalanceInDollars) {
final BigDecimal inCents = toCents(expectedBalanceInDollars);
eventually(
new Producer<GetAccountResponse>() {
@Override
public Observable<GetAccountResponse> produce() {
return Observable.just(BasicAuthUtils.doRestTemplateRequest(restTemplate,
baseUrl("/accounts/" + fromAccountId),
HttpMethod.GET,
GetAccountResponse.class));
}
},
new Verifier<GetAccountResponse>() {
@Override
public void verify(GetAccountResponse accountInfo) {
Assert.assertEquals(fromAccountId, accountInfo.getAccountId());
Assert.assertEquals(inCents, accountInfo.getBalance());
}
});
}
private void assertCustomerResponse(final String customerId, final CustomerInfo customerInfo) {
eventually(
new Producer<CustomerResponse>() {
@Override
public Observable<CustomerResponse> produce() {
return Observable.just(restTemplate.getForEntity(baseUrl("/customers/" + customerId), CustomerResponse.class).getBody());
}
},
new Verifier<CustomerResponse>() {
@Override
public void verify(CustomerResponse customerResponse) {
Assert.assertEquals(customerId, customerResponse.getId());
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
}
});
}
private void assertCustomerResponse(final String customerId, final CustomerInfo customerInfo) {
eventually(
new Producer<CustomerResponse>() {
@Override
public Observable<CustomerResponse> produce() {
return Observable.just(BasicAuthUtils.doRestTemplateRequest(restTemplate,
baseUrl("/customers/" + customerId),
HttpMethod.GET,
CustomerResponse.class));
}
},
new Verifier<CustomerResponse>() {
@Override
public void verify(CustomerResponse customerResponse) {
Assert.assertEquals(customerId, customerResponse.getId());
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
}
});
}
private CustomerInfo generateCustomerInfo() {
return new CustomerInfo(
new Name("John", "Doe"),
"current@email.com",
"000-00-0000",
"1-111-111-1111",
new Address("street 1",
"street 2",
"City",
"State",
"1111111")
);
}
private ToAccountInfo generateToAccountInfo() {
return new ToAccountInfo("11111111-11111111", "New Account", "John Doe");
}
private CustomerInfo generateCustomerInfo() {
return new CustomerInfo(
new Name("John", "Doe"),
"current@email.com",
"000-00-0000",
"1-111-111-1111",
new Address("street 1",
"street 2",
"City",
"State",
"1111111")
);
}
private ToAccountInfo generateToAccountInfo() {
return new ToAccountInfo("11111111-11111111", "New Account", "John Doe");
}
}