Implemented a simple Token Authentication for the /accounts endpoints, so not everyone can just create and delete accounts for fun. Closes #14

pull/21/head
Niclas Thobaben 2022-02-15 11:52:20 +01:00
parent 333ee23c42
commit f6e792b4e8
7 changed files with 78 additions and 9 deletions

View File

@ -50,7 +50,9 @@ public class AccountCrudTest {
@Test
void newAccountCanBeCreatedAndAccessedAfterwards() throws Exception {
AccountForm form = new AccountForm("my-company", List.of("vip@my-company.com"));
mockMvc.perform(post("/accounts").contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
mockMvc.perform(post("/accounts").header("x-nclazz-auth", "integration-token")
.contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
.andExpect(status().isCreated())
.andExpect(content().contentType(APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
@ -64,7 +66,7 @@ public class AccountCrudTest {
Account account = Account.of("retrievable-account", List.of("info@account.com"));
account = relay.saveAccount(account);
mockMvc.perform(get("/accounts/{guid}", account.getGuid()))
mockMvc.perform(get("/accounts/{guid}", account.getGuid()).header("x-nclazz-auth", "integration-token"))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType(APPLICATION_JSON))
.andExpect(jsonPath("$.name").value("retrievable-account"))
@ -79,7 +81,9 @@ public class AccountCrudTest {
AccountForm form = new AccountForm("updated-account-renamed", List.of("vip@account.com"));
mockMvc.perform(put("/accounts/{guid}", account.getGuid()).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
mockMvc.perform(put("/accounts/{guid}", account.getGuid()).header("x-nclazz-auth", "integration-token")
.contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType(APPLICATION_JSON))
.andExpect(jsonPath("$.name").value("updated-account-renamed"))
@ -90,7 +94,9 @@ public class AccountCrudTest {
@Test
void createAccountReturns400OnInvalidName() throws Exception {
AccountForm form = new AccountForm("-- totally invalid --", List.of("vip@my-company.com"));
mockMvc.perform(post("/accounts").contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
mockMvc.perform(post("/accounts").header("x-nclazz-auth", "integration-token")
.contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
.andExpect(status().is4xxClientError())
.andExpect(content().contentType(APPLICATION_JSON));
}
@ -98,8 +104,17 @@ public class AccountCrudTest {
@Test
void createAccountReturns400OnInvalidReceiver() throws Exception {
AccountForm form = new AccountForm("valid-name", List.of("valid@my-company.com", "in valid @my-company.com"));
mockMvc.perform(post("/accounts").contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
mockMvc.perform(post("/accounts").header("x-nclazz-auth", "integration-token")
.contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
.andExpect(status().is4xxClientError())
.andExpect(content().contentType(APPLICATION_JSON));
}
@Test
void whenNoAuthTokenIsProvidedReturns401() throws Exception {
AccountForm form = new AccountForm("valid-name", List.of("valid@my-company.com", "in valid @my-company.com"));
mockMvc.perform(post("/accounts").contentType(APPLICATION_JSON).content(mapper.writeValueAsString(form)))
.andExpect(status().is(401));
}
}

View File

@ -6,4 +6,6 @@ spring.mail.username=springboot
spring.mail.password=integration
spring.mail.host=localhost
spring.mail.port=25
spring.mail.protocol=smtp
spring.mail.protocol=smtp
nclazz.auth.token=integration-token

View File

@ -1,7 +1,10 @@
package de.nclazz.service.mailrelay;
import de.nclazz.service.mailrelay.adapter.web.SimpleAuthenticationFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import java.time.Clock;
@ -18,4 +21,14 @@ public class MailRelayApplication {
return Clock.systemDefaultZone();
}
@Bean
public FilterRegistrationBean<SimpleAuthenticationFilter> simpleAuthenticationFilter(@Value("${nclazz.auth.token}") String authenticationToken){
FilterRegistrationBean<SimpleAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new SimpleAuthenticationFilter(authenticationToken));
registrationBean.addUrlPatterns("/accounts/*");
return registrationBean;
}
}

View File

@ -20,14 +20,15 @@ import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("accounts")
@RequiredArgsConstructor
@RequestMapping("accounts")
public class AccountRestController {
private final Relay relay;
@PostMapping
public ResponseEntity<Account> addAccount(@RequestBody @Valid AccountForm form) {
Account account = form.toAccount();
account = this.relay.saveAccount(account);
return ResponseEntity.status(HttpStatus.CREATED).body(account);
@ -51,11 +52,13 @@ public class AccountRestController {
}
@PutMapping("{guid}")
public Account updateByGuid(@PathVariable("guid")UUID guid, @RequestBody @Valid AccountForm form) {
public Account updateByGuid(@PathVariable("guid")UUID guid,
@RequestBody @Valid AccountForm form) {
Account account = this.relay.findAccount(guid)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
account.setName(form.getName());
account.setReceivers(form.getReceivers());
return this.relay.saveAccount(account);
}
}

View File

@ -0,0 +1,33 @@
package de.nclazz.service.mailrelay.adapter.web;
import lombok.RequiredArgsConstructor;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RequiredArgsConstructor
public class SimpleAuthenticationFilter implements Filter {
private final String authenticationToken;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse res = (HttpServletResponse) servletResponse;
String token = req.getHeader("x-nclazz-auth");
if(!this.authenticationToken.equals(token)) {
res.setStatus(401);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}

View File

@ -1,3 +1,5 @@
# DDL auto schema generation (update, create, create-drop, none, validate)
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:h2:file:./test-db
nclazz.auth.token=

View File

@ -16,7 +16,6 @@ class AccountRestControllerTest {
@Test
void addAccountCreatesNewAccountAndReturnsIs() {
Relay relay = TestRelayBuilder.builder().build();
AccountRestController controller = new AccountRestController(relay);
@ -86,6 +85,7 @@ class AccountRestControllerTest {
@Test
void accountCanBeUpdated() {
String authToken = "auth-token";
Relay relay = TestRelayBuilder.builder().build();
AccountRestController controller = new AccountRestController(relay);
@ -103,4 +103,5 @@ class AccountRestControllerTest {
.extracting("name", "receivers")
.containsExactly("my-renamed-account", List.of("vip@my-company.com", "other@my-company.com"));
}
}