Compare commits

..

No commits in common. "d3751dda0f2a025e5952fda0b19320a5de9e9fb1" and "30aff2259ec43d8baded6d8a6f36519910a97b85" have entirely different histories.

5 changed files with 13 additions and 52 deletions

View File

@ -13,8 +13,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import java.security.Principal;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -30,12 +28,8 @@ public class CashCardController {
} }
@GetMapping("/{requestedId}") @GetMapping("/{requestedId}")
public ResponseEntity<CashCard> findById(@PathVariable Long requestedId, Principal principal) { public ResponseEntity<CashCard> findById(@PathVariable Long requestedId) {
CashCard cashCard = cashCardRepository.findByIdAndOwner( Optional<CashCard> cashCardOptional = cashCardRepository.findById(requestedId);
requestedId,
principal.getName()
);
Optional<CashCard> cashCardOptional = Optional.ofNullable(cashCard);
if (cashCardOptional.isPresent()) { if (cashCardOptional.isPresent()) {
return ResponseEntity.ok(cashCardOptional.get()); return ResponseEntity.ok(cashCardOptional.get());
@ -50,9 +44,8 @@ public class CashCardController {
// } // }
@GetMapping @GetMapping
public ResponseEntity<List<CashCard>> findAll(Pageable pageable, Principal principal) { public ResponseEntity<List<CashCard>> findAll(Pageable pageable) {
Page<CashCard> page = cashCardRepository.findByOwner( Page<CashCard> page = cashCardRepository.findAll(
principal.getName(),
PageRequest.of( PageRequest.of(
pageable.getPageNumber(), pageable.getPageNumber(),
pageable.getPageSize(), pageable.getPageSize(),
@ -63,15 +56,13 @@ public class CashCardController {
} }
@PostMapping @PostMapping
private ResponseEntity<Void> createCashCard(@RequestBody CashCard newCashCardRequest, UriComponentsBuilder ucb, Principal principal) { private ResponseEntity<Void> createCashCard(@RequestBody CashCard newCashCardRequest, UriComponentsBuilder ucb) {
CashCard cashCardWithOwner = new CashCard(null, newCashCardRequest.amount(), principal.getName()); CashCard savedCashCard = cashCardRepository.save(newCashCardRequest); // CRUD - Create
CashCard savedCashCard = cashCardRepository.save(cashCardWithOwner); // CRUD - Create
URI locationOfNewCashCard = ucb URI locationOfNewCashCard = ucb
.path("cashcards/{id}") .path("cashcards/{id}")
.buildAndExpand(savedCashCard.id()) .buildAndExpand(savedCashCard.id())
.toUri(); .toUri();
return ResponseEntity.created(locationOfNewCashCard).build(); return ResponseEntity.created(locationOfNewCashCard).build();
} }
} }

View File

@ -2,15 +2,10 @@ package djmil.cashcard;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
public interface public interface
CashCardRepository CashCardRepository
extends extends
CrudRepository<CashCard, Long>, CrudRepository<CashCard, Long>,
PagingAndSortingRepository<CashCard, Long> PagingAndSortingRepository<CashCard, Long> {
{
CashCard findByIdAndOwner(Long id, String owner);
Page<CashCard> findByOwner(String owner, PageRequest amount);
} }

View File

@ -18,8 +18,7 @@ public class SecurityConfig {
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests() http.authorizeHttpRequests()
.requestMatchers("/cashcards/**") .requestMatchers("/cashcards/**")
//.authenticated() .authenticated()
.hasRole("CARD-OWNER") // <<-- enable RBAC
.and() .and()
.csrf().disable() .csrf().disable()
.httpBasic(); .httpBasic();
@ -35,19 +34,11 @@ public class SecurityConfig {
@Bean @Bean
public UserDetailsService testOnlyUsers(PasswordEncoder passwordEncoder) { public UserDetailsService testOnlyUsers(PasswordEncoder passwordEncoder) {
User.UserBuilder users = User.builder(); User.UserBuilder users = User.builder();
UserDetails sarah = users UserDetails sarah = users
.username("sarah1") .username("sarah1")
.password(passwordEncoder.encode("abc123")) .password(passwordEncoder.encode("abc123"))
.roles("CARD-OWNER") // new role .roles() // No roles for now
.build(); .build();
return new InMemoryUserDetailsManager(sarah);
UserDetails hankOwnsNoCards = users
.username("hank-owns-no-cards")
.password(passwordEncoder.encode("qrs456"))
.roles("NON-OWNER") // new role
.build();
return new InMemoryUserDetailsManager(sarah, hankOwnsNoCards);
} }
} }

View File

@ -59,13 +59,14 @@ class CashcardApplicationTests {
@Test @Test
@DirtiesContext @DirtiesContext
void shouldCreateANewCashCard() { void shouldCreateANewCashCard() {
CashCard newCashCard = new CashCard(null, 250.00, null); CashCard newCashCard = new CashCard(null, 250.00, "sarah1");
ResponseEntity<Void> createResponse = restTemplate ResponseEntity<Void> createResponse = restTemplate
.withBasicAuth("sarah1", "abc123") .withBasicAuth("sarah1", "abc123")
.postForEntity("/cashcards", newCashCard, Void.class ); .postForEntity("/cashcards", newCashCard, Void.class );
assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED); assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
// Validate created CashCard // Validate created CashCard
URI locationOfNewCashCard = createResponse.getHeaders().getLocation(); URI locationOfNewCashCard = createResponse.getHeaders().getLocation();
ResponseEntity<String> getResponse = restTemplate ResponseEntity<String> getResponse = restTemplate
@ -153,20 +154,4 @@ class CashcardApplicationTests {
.getForEntity("/cashcards/99", String.class); .getForEntity("/cashcards/99", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
} }
@Test
void shouldRejectUsersWhoAreNotCardOwners() {
ResponseEntity<String> response = restTemplate
.withBasicAuth("hank-owns-no-cards", "qrs456")
.getForEntity("/cashcards/99", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void shouldNotAllowAccessToCashCardsTheyDoNotOwn() {
ResponseEntity<String> response = restTemplate
.withBasicAuth("sarah1", "abc123")
.getForEntity("/cashcards/102", String.class); // <<-- kumar2's data
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
} }

View File

@ -1,4 +1,3 @@
INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (99, 123.45, 'sarah1'); INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (99, 123.45, 'sarah1');
INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (100, 1.00, 'sarah1'); INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (100, 1.00, 'sarah1');
INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (101, 150.00, 'sarah1'); INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (101, 150.00, 'sarah1');
INSERT INTO CASH_CARD(ID, AMOUNT, OWNER) VALUES (102, 200.00, 'kumar2');