Skip to content
Permalink
Browse files

User registration functionality applied. All API endpoints except for…

… the one for user registration are secured. CORS enabled only on the development environment.
  • Loading branch information
little-pinecone committed Jan 7, 2019
1 parent 1d6b7c7 commit 40e2d0ec5745cb0605a5c59525b8628cf2df0dc7
@@ -19,6 +19,28 @@ You can build the application with:
$ mvn clean install
```

### Registering a test user

POST endpoint:

```bash
http://localhost:8080/api/users
```
Body:

```json
{
"userCredentials": {
"username": "user",
"password": "test"
}
}
```

### Running in production

Set up the `apiUrl` const in `frontend/src/main/angular/src/environments/environment.prod.ts`.

## Built With

* Java 11
@@ -31,6 +53,7 @@ $ mvn clean install
## Overview and technical features

The project currently serves a dummy login page and hard-coded pastry data returned from the API.
It allows registering new users.

## Running tests

@@ -23,6 +23,11 @@
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@@ -0,0 +1,14 @@
package in.keepgrowing.jwtspringbootangularscaffolding.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class PasswordEncoderConfig {

@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@@ -0,0 +1,44 @@
package in.keepgrowing.jwtspringbootangularscaffolding.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.http.HttpServletResponse;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Value("${cors.enabled:false}")
private boolean corsEnabled;

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
applyCors(httpSecurity)
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedResponse())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/users").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
}

private HttpSecurity applyCors(HttpSecurity httpSecurity) throws Exception {
if (corsEnabled) {
return httpSecurity.cors().and();
} else {
return httpSecurity;
}
}

private AuthenticationEntryPoint unauthorizedResponse() {
return (req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
@@ -0,0 +1,22 @@
package in.keepgrowing.jwtspringbootangularscaffolding.user;

import in.keepgrowing.jwtspringbootangularscaffolding.user.model.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/users")
public class UserController {
private UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping
public User register(@RequestBody User user) {
return userService.register(user);
}
}
@@ -0,0 +1,30 @@
package in.keepgrowing.jwtspringbootangularscaffolding.user;

import in.keepgrowing.jwtspringbootangularscaffolding.user.model.User;
import in.keepgrowing.jwtspringbootangularscaffolding.user.model.UserRepository;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserService {

private static final String DEFAULT_ROLE = "ROLE_USER";
private UserRepository userRepository;
private BCryptPasswordEncoder encoder;

public UserService(UserRepository userRepository, BCryptPasswordEncoder encoder) {
this.userRepository = userRepository;
this.encoder = encoder;
}

public User register(User user) {
setPasswordAndRole(user);
return userRepository.save(user);
}

private void setPasswordAndRole(User user) {
user.getUserCredentials()
.setPassword(encoder.encode(user.getUserCredentials().getPassword()));
user.getUserCredentials().setRole(DEFAULT_ROLE);
}
}
@@ -0,0 +1,52 @@
package in.keepgrowing.jwtspringbootangularscaffolding.user.model;

import javax.persistence.*;
import java.util.Objects;

@Entity
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Embedded
private UserCredentials userCredentials;

protected User() {
}

public User(UserCredentials userCredentials) {
this.userCredentials = userCredentials;
}

public Long getId() {
return id;
}

public UserCredentials getUserCredentials() {
return userCredentials;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(userCredentials, user.userCredentials);
}

@Override
public int hashCode() {
return Objects.hash(id, userCredentials);
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", userCredentials=" + userCredentials +
'}';
}
}
@@ -0,0 +1,72 @@
package in.keepgrowing.jwtspringbootangularscaffolding.user.model;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.validation.constraints.NotBlank;
import java.util.Objects;

@Embeddable
public class UserCredentials {

@NotBlank(message = "Provide a username")
@Column(unique=true)
private String username;

@NotBlank(message = "Provide a password")
private String password;

private String role;

UserCredentials() {
}

public UserCredentials(String username, String password, String role) {
this.username = username;
this.password = password;
this.role = role;
}

public String getUsername() {
return username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getRole() {
return role;
}

public void setRole(String role) {
this.role = role;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserCredentials that = (UserCredentials) o;
return Objects.equals(username, that.username) &&
Objects.equals(password, that.password) &&
Objects.equals(role, that.role);
}

@Override
public int hashCode() {
return Objects.hash(username, password, role);
}

@Override
public String toString() {
return "UserCredentials{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", role='" + role + '\'' +
'}';
}
}
@@ -0,0 +1,6 @@
package in.keepgrowing.jwtspringbootangularscaffolding.user.model;

import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> {
}
@@ -0,0 +1 @@
cors.enabled=true
@@ -0,0 +1,53 @@
package in.keepgrowing.jwtspringbootangularscaffolding.user;

import com.fasterxml.jackson.databind.ObjectMapper;
import in.keepgrowing.jwtspringbootangularscaffolding.user.model.User;
import in.keepgrowing.jwtspringbootangularscaffolding.user.model.UserCredentials;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.is;
import static org.mockito.BDDMockito.given;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {

private JacksonTester<User> userJacksonTester;
@MockBean
private UserService userService;
@Autowired
private MockMvc mvc;

@Before
public void setUp() {
JacksonTester.initFields(this, new ObjectMapper());
}

@Test
public void register() throws Exception {
UserCredentials userCredentials = new UserCredentials("user", "password", "USER_ROLE");
User user = new User(userCredentials);
given(userService.register(user))
.willReturn(user);

mvc.perform(post("/api/users")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(userJacksonTester.write(user).getJson()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.userCredentials.username", is(user.getUserCredentials().getUsername())));
}
}

0 comments on commit 40e2d0e

Please sign in to comment.
You can’t perform that action at this time.