Compare commits
3 Commits
ca46b46029
...
30aff2259e
Author | SHA1 | Date | |
---|---|---|---|
30aff2259e | |||
7963542c9b | |||
6cd4088522 |
@ -21,6 +21,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'org.springframework.data:spring-data-jdbc'
|
implementation 'org.springframework.data:spring-data-jdbc'
|
||||||
testImplementation 'com.h2database:h2'
|
testImplementation 'com.h2database:h2'
|
||||||
|
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
|
@ -2,5 +2,5 @@ package djmil.cashcard;
|
|||||||
|
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
|
|
||||||
public record CashCard(@Id Long id, Double amount) {
|
public record CashCard(@Id Long id, Double amount, String owner) {
|
||||||
}
|
}
|
||||||
|
44
src/main/java/djmil/cashcard/SecurityConfig.java
Normal file
44
src/main/java/djmil/cashcard/SecurityConfig.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package djmil.cashcard;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
http.authorizeHttpRequests()
|
||||||
|
.requestMatchers("/cashcards/**")
|
||||||
|
.authenticated()
|
||||||
|
.and()
|
||||||
|
.csrf().disable()
|
||||||
|
.httpBasic();
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public UserDetailsService testOnlyUsers(PasswordEncoder passwordEncoder) {
|
||||||
|
User.UserBuilder users = User.builder();
|
||||||
|
UserDetails sarah = users
|
||||||
|
.username("sarah1")
|
||||||
|
.password(passwordEncoder.encode("abc123"))
|
||||||
|
.roles() // No roles for now
|
||||||
|
.build();
|
||||||
|
return new InMemoryUserDetailsManager(sarah);
|
||||||
|
}
|
||||||
|
}
|
@ -25,9 +25,9 @@ public class CashCardJsonTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
cashCards = Arrays.array(
|
cashCards = Arrays.array(
|
||||||
new CashCard(99L, 123.45),
|
new CashCard(99L, 123.45, "sarah1"),
|
||||||
new CashCard(100L, 1.00),
|
new CashCard(100L, 1.00, "sarah1"),
|
||||||
new CashCard(101L, 150.00));
|
new CashCard(101L, 150.00, "sarah1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -37,7 +37,7 @@ public class CashCardJsonTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void cashCardSerializationTest() throws IOException {
|
public void cashCardSerializationTest() throws IOException {
|
||||||
CashCard cashCard = new CashCard(99L, 123.45);
|
CashCard cashCard = new CashCard(99L, 123.45, "sarah1");
|
||||||
|
|
||||||
assertThat(json.write(cashCard)).isStrictlyEqualToJson("expected.json");
|
assertThat(json.write(cashCard)).isStrictlyEqualToJson("expected.json");
|
||||||
|
|
||||||
@ -55,11 +55,12 @@ public class CashCardJsonTest {
|
|||||||
String expected = """
|
String expected = """
|
||||||
{
|
{
|
||||||
"id":1000,
|
"id":1000,
|
||||||
"amount":67.89
|
"amount":67.89,
|
||||||
|
"owner": "sarah1"
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
assertThat(json.parse(expected)).isEqualTo(new CashCard(1000L, 67.89));
|
assertThat(json.parse(expected)).isEqualTo(new CashCard(1000L, 67.89, "sarah1"));
|
||||||
|
|
||||||
assertThat(json.parseObject(expected).id()).isEqualTo(1000);
|
assertThat(json.parseObject(expected).id()).isEqualTo(1000);
|
||||||
assertThat(json.parseObject(expected).amount()).isEqualTo(67.89);
|
assertThat(json.parseObject(expected).amount()).isEqualTo(67.89);
|
||||||
@ -74,9 +75,9 @@ public class CashCardJsonTest {
|
|||||||
void cashCardListDeserializationTest() throws IOException {
|
void cashCardListDeserializationTest() throws IOException {
|
||||||
String expected="""
|
String expected="""
|
||||||
[
|
[
|
||||||
{ "id": 99, "amount": 123.45 },
|
{ "id": 99, "amount": 123.45, "owner": "sarah1" },
|
||||||
{ "id": 100, "amount": 1.00 },
|
{ "id": 100, "amount": 1.00, "owner": "sarah1" },
|
||||||
{ "id": 101, "amount": 150.00 }
|
{ "id": 101, "amount": 150.00, "owner": "sarah1" }
|
||||||
]
|
]
|
||||||
""";
|
""";
|
||||||
assertThat(jsonList.parse(expected)).isEqualTo(cashCards);
|
assertThat(jsonList.parse(expected)).isEqualTo(cashCards);
|
||||||
|
@ -31,7 +31,9 @@ class CashcardApplicationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnACashCardWhenDataIsSaved() {
|
void shouldReturnACashCardWhenDataIsSaved() {
|
||||||
ResponseEntity<String> response = restTemplate.getForEntity("/cashcards/99", String.class);
|
ResponseEntity<String> response = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "abc123")
|
||||||
|
.getForEntity("/cashcards/99", String.class);
|
||||||
|
|
||||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
|
||||||
@ -46,7 +48,9 @@ class CashcardApplicationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldNotReturnACashCardWithAnUnknownId() {
|
void shouldNotReturnACashCardWithAnUnknownId() {
|
||||||
ResponseEntity<String> response = restTemplate.getForEntity("/cashcards/1000", String.class);
|
ResponseEntity<String> response = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "abc123")
|
||||||
|
.getForEntity("/cashcards/1000", String.class);
|
||||||
|
|
||||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||||
assertThat(response.getBody()).isBlank();
|
assertThat(response.getBody()).isBlank();
|
||||||
@ -55,16 +59,21 @@ class CashcardApplicationTests {
|
|||||||
@Test
|
@Test
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
void shouldCreateANewCashCard() {
|
void shouldCreateANewCashCard() {
|
||||||
CashCard newCashCard = new CashCard(null, 250.00);
|
CashCard newCashCard = new CashCard(null, 250.00, "sarah1");
|
||||||
ResponseEntity<Void> createResponse = restTemplate.postForEntity("/cashcards", newCashCard, Void.class );
|
ResponseEntity<Void> createResponse = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "abc123")
|
||||||
|
.postForEntity("/cashcards", newCashCard, Void.class );
|
||||||
|
|
||||||
assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
|
assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
|
||||||
|
|
||||||
|
|
||||||
|
// Validate created CashCard
|
||||||
URI locationOfNewCashCard = createResponse.getHeaders().getLocation();
|
URI locationOfNewCashCard = createResponse.getHeaders().getLocation();
|
||||||
ResponseEntity<String> getResponse = restTemplate.getForEntity(locationOfNewCashCard, String.class);
|
ResponseEntity<String> getResponse = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "abc123")
|
||||||
|
.getForEntity(locationOfNewCashCard, String.class);
|
||||||
assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
|
||||||
// Validate created CashCard JSON
|
|
||||||
DocumentContext documentContext = JsonPath.parse(getResponse.getBody());
|
DocumentContext documentContext = JsonPath.parse(getResponse.getBody());
|
||||||
Number id = documentContext.read("$.id");
|
Number id = documentContext.read("$.id");
|
||||||
Double amount = documentContext.read("$.amount");
|
Double amount = documentContext.read("$.amount");
|
||||||
@ -75,7 +84,9 @@ class CashcardApplicationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnAllCashCardsWhenListIsRequested() {
|
void shouldReturnAllCashCardsWhenListIsRequested() {
|
||||||
ResponseEntity<String> response = restTemplate.getForEntity("/cashcards", String.class);
|
ResponseEntity<String> response = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "abc123")
|
||||||
|
.getForEntity("/cashcards", String.class);
|
||||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
|
||||||
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
||||||
@ -91,7 +102,9 @@ class CashcardApplicationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnAPageOfCashCards() {
|
void shouldReturnAPageOfCashCards() {
|
||||||
ResponseEntity<String> response = restTemplate.getForEntity("/cashcards?page=0&size=1", String.class);
|
ResponseEntity<String> response = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "abc123")
|
||||||
|
.getForEntity("/cashcards?page=0&size=1", String.class);
|
||||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
|
||||||
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
||||||
@ -101,7 +114,9 @@ class CashcardApplicationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnASortedPageOfCashCards() {
|
void shouldReturnASortedPageOfCashCards() {
|
||||||
ResponseEntity<String> response = restTemplate.getForEntity("/cashcards?page=0&size=1&sort=amount,desc", String.class);
|
ResponseEntity<String> response = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "abc123")
|
||||||
|
.getForEntity("/cashcards?page=0&size=1&sort=amount,desc", String.class);
|
||||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
|
||||||
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
||||||
@ -114,7 +129,9 @@ class CashcardApplicationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnASortedPageOfCashCardsWithNoParametersAndUseDefaultValues() {
|
void shouldReturnASortedPageOfCashCardsWithNoParametersAndUseDefaultValues() {
|
||||||
ResponseEntity<String> response = restTemplate.getForEntity("/cashcards", String.class);
|
ResponseEntity<String> response = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "abc123")
|
||||||
|
.getForEntity("/cashcards", String.class);
|
||||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
|
||||||
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
||||||
@ -124,4 +141,17 @@ class CashcardApplicationTests {
|
|||||||
JSONArray amounts = documentContext.read("$..amount");
|
JSONArray amounts = documentContext.read("$..amount");
|
||||||
assertThat(amounts).containsExactly(1.00, 123.45, 150.00);
|
assertThat(amounts).containsExactly(1.00, 123.45, 150.00);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotReturnACashCardWhenUsingBadCredentials() {
|
||||||
|
ResponseEntity<String> response = restTemplate
|
||||||
|
.withBasicAuth("BAD-USER", "abc123")
|
||||||
|
.getForEntity("/cashcards/99", String.class);
|
||||||
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||||
|
|
||||||
|
response = restTemplate
|
||||||
|
.withBasicAuth("sarah1", "BAD-PASSWORD")
|
||||||
|
.getForEntity("/cashcards/99", String.class);
|
||||||
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
INSERT INTO CASH_CARD(ID, AMOUNT) VALUES (99, 123.45);
|
INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (99, 123.45, 'sarah1');
|
||||||
INSERT INTO CASH_CARD(ID, AMOUNT) VALUES (100, 1.00);
|
INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (100, 1.00, 'sarah1');
|
||||||
INSERT INTO CASH_CARD(ID, AMOUNT) VALUES (101, 150.00);
|
INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (101, 150.00, 'sarah1');
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"id": 99,
|
"id": 99,
|
||||||
"amount": 123.45
|
"amount": 123.45,
|
||||||
|
"owner": "sarah1"
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
[
|
[
|
||||||
{ "id": 99, "amount": 123.45 },
|
{ "id": 99, "amount": 123.45, "owner": "sarah1" },
|
||||||
{ "id": 100, "amount": 1.0 },
|
{ "id": 100, "amount": 1.0, "owner": "sarah1" },
|
||||||
{ "id": 101, "amount": 150.0 }
|
{ "id": 101, "amount": 150.0, "owner": "sarah1" }
|
||||||
]
|
]
|
@ -1,5 +1,6 @@
|
|||||||
CREATE TABLE cash_card
|
CREATE TABLE cash_card
|
||||||
(
|
(
|
||||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
ID BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
AMOUNT NUMBER NOT NULL DEFAULT 0
|
AMOUNT NUMBER NOT NULL DEFAULT 0,
|
||||||
|
OWNER VARCHAR(256) NOT NULL
|
||||||
);
|
);
|
Loading…
Reference in New Issue
Block a user