Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,16 @@
<!-- </dependency>-->

<!-- Secure backend API -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-security</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8080");
registry
.addMapping("/**")
.allowedOrigins("http://localhost:9000")
.allowedMethods("OPTIONS", "PUT", "DELETE", "GET", "POST", "PATCH");
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class LibraryApplication {

public static void main(String[] args) {
SpringApplication.run(LibraryApplication.class, args);
ConfigurableApplicationContext ctx = SpringApplication.run(LibraryApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.tartu.library.auth.domain.model;

import com.tartu.library.person.domain.model.Person;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.*;
import java.util.Collection;
import java.util.Set;
import java.util.UUID;

@Entity
@Data
@NoArgsConstructor(force = true, access = AccessLevel.PROTECTED)
public class AuthUser implements UserDetails {
/** User is reserved word in Postgres */
String username;
String password;
@OneToOne Person person;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;

@Transient private String passwordConfirm;

@ManyToMany(fetch = FetchType.EAGER)
private Set<Role> roles;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.tartu.library.auth.domain.model;

import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;

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

@Entity
@NoArgsConstructor
public class Role implements GrantedAuthority {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
Long id;

private String name;

@Transient
@ManyToMany(mappedBy = "roles")
private Set<AuthUser> people;

public Role(Long id) {
this.id = id;
}

public Role(Long id, String name) {
this.id = id;
this.name = name;
}

@Override
public String getAuthority() {
return name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.tartu.library.auth.domain.repository;

import com.tartu.library.auth.domain.model.AuthUser;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AuthUserRepository extends JpaRepository<AuthUser, Long> {
AuthUser findByUsername(String username);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.tartu.library.auth.domain.repository;

import com.tartu.library.auth.domain.model.Role;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RoleRepository extends JpaRepository<Role, Long> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.tartu.library.auth.rest;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class AuthenticationController {
@GetMapping("/api/authenticate")
public List<String> authenticate() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
List<String> roles = new ArrayList<>();
if (principal instanceof UserDetails) {
UserDetails details = (UserDetails) principal;
for (GrantedAuthority authority : details.getAuthorities())
roles.add(authority.getAuthority());
}
return roles;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.tartu.library.auth.rest;

import com.tartu.library.auth.domain.model.AuthUser;
import com.tartu.library.auth.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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 {
@Autowired UserService userService;

@PostMapping()
public void createUser(@RequestBody AuthUser authUser) {
userService.save(authUser);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.tartu.library.auth.security;

import com.tartu.library.auth.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfigurator extends WebSecurityConfigurerAdapter {

@Qualifier("userService")
@Autowired
UserService userService;

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.cors()
.and()
.authorizeRequests()
.antMatchers("/h2-console/**").permitAll()
.antMatchers("/api/users").permitAll()
.antMatchers("/api/authenticate").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()
.antMatchers("/api/**").authenticated()
.and().httpBasic();
http.headers().frameOptions().disable();
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.tartu.library.auth.service;

import com.tartu.library.auth.domain.model.AuthUser;
import com.tartu.library.auth.domain.repository.AuthUserRepository;
import com.tartu.library.auth.domain.repository.RoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;

@Service
public class UserService implements UserDetailsService {
@Autowired BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired RoleRepository roleRepository;
@Autowired AuthUserRepository authUserRepository;

public void save(AuthUser authUser) {
authUser.setPassword(bCryptPasswordEncoder.encode(authUser.getPassword()));
authUser.setRoles(new HashSet<>(roleRepository.findAll()));
authUserRepository.save(authUser);
}

@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AuthUser authUser = authUserRepository.findByUsername(username);
if (authUser == null) throw new UsernameNotFoundException(username);
return authUser;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.hateoas.CollectionModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

Expand Down Expand Up @@ -40,6 +41,7 @@ public BookEntryDTO retrieveBook(@PathVariable UUID uuid) {
}

@PatchMapping("{uuid}")
@Secured({"ADMIN"})
public ResponseEntity<BookEntryDTO> modifyBook(@PathVariable UUID uuid) {
logger.info(String.format("Modifying Book Entry (%s)", uuid.toString()));
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.hateoas.CollectionModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;
Expand All @@ -35,26 +36,30 @@ public BookItemDTO retrieveBookItem(@PathVariable UUID uuid) {
}

@DeleteMapping("{uuid}")
@Secured({"ADMIN"})
public ResponseEntity<Void> deleteBookItem(@PathVariable UUID uuid) {
logger.info(String.format("Deleting Book Item (%s)", uuid.toString()));
bookService.deleteBookItem(uuid);
return new ResponseEntity<>(HttpStatus.OK);
}

@PatchMapping("{uuid}/borrow")
@Secured({"AUTH_USER"})
public BookItemDTO borrowBook(@PathVariable UUID uuid, @RequestParam UUID person_uuid)
throws InvalidBookStatusException {
logger.info(String.format("Borrowing Book Item (%s)", uuid.toString()));
return bookService.borrowBook(uuid, person_uuid);
}

@PatchMapping("{uuid}/return")
@Secured({"AUTH_USER"})
public BookItemDTO returnBook(@PathVariable UUID uuid) throws InvalidBookStatusException {
logger.info(String.format("Returning Book Item (%s)", uuid.toString()));
return bookService.returnBook(uuid);
}

@GetMapping("{uuid}/logs")
@Secured({"AUTH_USER"})
public CollectionModel<BorrowLogDTO> retrieveBorrowLogs(@PathVariable UUID uuid) {
logger.info(String.format("Retrieving Borrow Logs from Book Item (%s)", uuid.toString()));
return bookService.retrieveBorrowLogsByBookItem(uuid);
Expand Down
10 changes: 10 additions & 0 deletions backend/src/main/resources/data.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
insert into person (id, name) values (1, 'Admin');

insert into role (id, name) values (1, 'ADMIN');
insert into role (id, name) values (2, 'AUTH_USER');

insert into auth_user (id, username, password, person_id) values (1, 'admin', '$2a$10$/BokLf7JuxdOZZK5hEUzauPOUnfyiWZ.P3SgnsBaE14FD.kjKd/U2', 1);
insert into auth_user (id, username, password) values (2, 'user', '$2a$10$vIQpONjeg31v..Dat4L6BO.u.hliQ5e0NrJQU.JMWhA/3R.ZRmZl2');

insert into auth_user_roles (auth_user_id, roles_id) values (1, 1);
insert into auth_user_roles (auth_user_id, roles_id) values (2, 2);