Skip to content
Snippets Groups Projects
Commit 17546304 authored by Tomas Madeja's avatar Tomas Madeja
Browse files

feat: add basic auth

parent da1259d7
No related branches found
No related tags found
No related merge requests found
Showing
with 387 additions and 26 deletions
package cz.muni.fi.pa165.icehockeymanager.dto;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
@Getter @Setter
public class UserAuthRequestDto {
@NotBlank
String username;
@NotBlank
String password;
}
......@@ -15,7 +15,7 @@ public final class UserDto {
private Long id;
@NotBlank
private Long username;
private String username;
@NotNull
private Roles role;
......
......@@ -6,6 +6,6 @@ public enum Roles {
@Override
public String toString() {
return this.name().toLowerCase();
return this.name().toUpperCase();
}
}
......@@ -41,7 +41,7 @@
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
@REM Execute a account defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
......
package cz.muni.fi.pa165.icehockeymanager.dao;
import cz.muni.fi.pa165.icehockeymanager.model.Account;
import cz.muni.fi.pa165.icehockeymanager.model.Team;
import cz.muni.fi.pa165.icehockeymanager.model.User;
import org.springframework.stereotype.Repository;
import javax.persistence.NoResultException;
......@@ -14,11 +14,11 @@ public class TeamDao extends AbstractDao<Team> {
super(Team.class);
}
public Optional<Team> findByManager(User manager) {
public Optional<Team> findByManager(Account manager) {
Team team = null;
try {
team = (Team) entityManager
.createNativeQuery(
.createQuery(
"SELECT t FROM Team t WHERE t.managerId = :managerId",
Team.class
)
......
package cz.muni.fi.pa165.icehockeymanager.dao;
import cz.muni.fi.pa165.icehockeymanager.model.User;
import cz.muni.fi.pa165.icehockeymanager.model.Account;
import org.springframework.stereotype.Repository;
import javax.persistence.NoResultException;
import java.util.Optional;
@Repository
public class UserAuthDao extends AbstractDao<User>{
public class UserAuthDao extends AbstractDao<Account>{
public UserAuthDao() {
super(User.class);
super(Account.class);
}
public Optional<User> findByUsername(String username) {
User user = null;
public Optional<Account> findByUsername(String username) {
Account user = null;
try {
user = (User) entityManager
.createNativeQuery(
"SELECT u FROM User u WHERE u.username = :username",
User.class
user = (Account) entityManager
.createQuery(
"SELECT a FROM Account a WHERE a.username = :username",
Account.class
)
.setParameter("username", username)
.getSingleResult();
......
......@@ -8,18 +8,15 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Entity
@Table
@Getter
@Setter
public class User {
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
......@@ -39,9 +36,9 @@ public class User {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(getUsername(), user.getUsername());
if (!(o instanceof Account)) return false;
Account account = (Account) o;
return Objects.equals(getUsername(), account.getUsername());
}
@Override
......
......@@ -43,6 +43,13 @@
<version>5.4.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
......@@ -191,6 +198,14 @@
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.16.0</version>
</dependency>
</dependencies>
</dependencyManagement>
......
......@@ -37,6 +37,24 @@
<artifactId>api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.16.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
......
......@@ -2,11 +2,25 @@ package cz.muni.fi.pa165.icehockeymanager.rest.config;
import cz.muni.fi.pa165.icehockeymanager.config.ApplicationConfig;
import cz.muni.fi.pa165.icehockeymanager.config.PersistanceApplicationConfig;
import cz.muni.fi.pa165.icehockeymanager.rest.security.JWTAuthenticationFilter;
import cz.muni.fi.pa165.icehockeymanager.rest.security.JWTAuthorizationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import static cz.muni.fi.pa165.icehockeymanager.rest.security.SecurityConstants.SIGN_UP_URL;
@Configuration
@EnableTransactionManagement
......@@ -14,4 +28,5 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@Import(ApplicationConfig.class)
@ComponentScan(basePackages = "cz.muni.fi.pa165.icehockeymanager")
public class RestConfiguration {
}
......@@ -3,6 +3,7 @@ package cz.muni.fi.pa165.icehockeymanager.rest.controller;
import cz.muni.fi.pa165.icehockeymanager.dto.*;
import cz.muni.fi.pa165.icehockeymanager.facades.LeagueManagerFacade;
import cz.muni.fi.pa165.icehockeymanager.facades.TeamManagerFacade;
import cz.muni.fi.pa165.icehockeymanager.facades.UserAuthFacade;
import cz.muni.fi.pa165.icehockeymanager.facades.UserFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
......@@ -17,12 +18,25 @@ public class IceHockeyManagerApi {
private final UserFacade userFacade;
private final LeagueManagerFacade leagueManagerFacade;
private final TeamManagerFacade teamManagerFacade;
private final UserAuthFacade userAuthFacade;
@Autowired
public IceHockeyManagerApi(UserFacade userFacade, LeagueManagerFacade leagueManagerFacade, TeamManagerFacade teamManagerFacade) {
public IceHockeyManagerApi(
UserFacade userFacade,
LeagueManagerFacade leagueManagerFacade,
TeamManagerFacade teamManagerFacade,
UserAuthFacade userAuthFacade
) {
this.userFacade = userFacade;
this.leagueManagerFacade = leagueManagerFacade;
this.teamManagerFacade = teamManagerFacade;
this.userAuthFacade = userAuthFacade;
}
@GetMapping("/auth")
public final void userAuthRequest(@Valid UserAuthRequestDto authForm) {
userAuthFacade.authenticateUser(authForm.getUsername(), authForm.getPassword());
}
@GetMapping("/player/free")
......
package cz.muni.fi.pa165.icehockeymanager.rest.demo;
import cz.muni.fi.pa165.icehockeymanager.dto.TeamCreateDto;
import cz.muni.fi.pa165.icehockeymanager.facades.LeagueManagerFacade;
import cz.muni.fi.pa165.icehockeymanager.facades.UserAuthFacade;
import cz.muni.fi.pa165.icehockeymanager.facades.UserFacade;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class DataLoader implements ApplicationRunner {
private final UserAuthFacade userAuth;
private final LeagueManagerFacade leagueManager;
private final UserFacade userFacade;
public DataLoader(UserAuthFacade userAuth, LeagueManagerFacade leagueManager, UserFacade userFacade) {
this.userAuth = userAuth;
this.leagueManager = leagueManager;
this.userFacade = userFacade;
}
@Override
public void run(ApplicationArguments args) throws Exception {
leagueManager.createNewTeam(createTeam("Toronto Maple Leafs"));
var teams = userFacade.getTeamsInLeague();
var team = teams.stream().findFirst().orElseThrow();
userAuth.createTeamManager("team_manager", "1234", team.getId().intValue());
}
private static TeamCreateDto createTeam(String name) {
var team = new TeamCreateDto();
team.setName(name);
return team;
}
}
package cz.muni.fi.pa165.icehockeymanager.rest.security;
import cz.muni.fi.pa165.icehockeymanager.facades.UserAuthFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
@Component
public class AuthenticationProviderPlugin implements AuthenticationProvider {
private final UserAuthFacade userAuthFacade;
@Autowired
public AuthenticationProviderPlugin(UserAuthFacade userAuthFacade) {
this.userAuthFacade = userAuthFacade;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
userAuthFacade.authenticateUser(username, password).orElseThrow(() -> new BadCredentialsException(username));
return new UsernamePasswordAuthenticationToken(
username, password, new ArrayList<>());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
package cz.muni.fi.pa165.icehockeymanager.rest.security;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import cz.muni.fi.pa165.icehockeymanager.dto.UserAuthRequestDto;
import cz.muni.fi.pa165.icehockeymanager.facades.UserAuthFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static cz.muni.fi.pa165.icehockeymanager.rest.security.SecurityConstants.EXPIRATION_TIME;
import static cz.muni.fi.pa165.icehockeymanager.rest.security.SecurityConstants.SECRET;
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
private UserAuthFacade userAuthFacade;
public JWTAuthenticationFilter(UserAuthFacade userAuthFacade) {
this.userAuthFacade = userAuthFacade;
setFilterProcessesUrl("/api/services/controller/user/login");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
UserAuthRequestDto creds = new ObjectMapper()
.readValue(req.getInputStream(), UserAuthRequestDto.class);
logger.info(String.format("Log in attempt by %s", creds.getUsername()));
var user = userAuthFacade.authenticateUser(
creds.getUsername(),
creds.getPassword()
).orElseThrow(() -> new BadCredentialsException(creds.getUsername()));
return new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
List.of(
new SimpleGrantedAuthority(user.getRole().toString())
)
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException {
logger.info(String.format("Generate JWT token for %s", auth.getPrincipal()));
String token = JWT.create()
.withSubject(((String) auth.getPrincipal()))
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(Algorithm.HMAC512(SECRET.getBytes()));
String body = ((String) auth.getPrincipal()) + " " + token;
res.getWriter().write(body);
res.getWriter().flush();
}
}
\ No newline at end of file
package cz.muni.fi.pa165.icehockeymanager.rest.security;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import static cz.muni.fi.pa165.icehockeymanager.rest.security.SecurityConstants.HEADER_STRING;
import static cz.muni.fi.pa165.icehockeymanager.rest.security.SecurityConstants.SECRET;
import static cz.muni.fi.pa165.icehockeymanager.rest.security.SecurityConstants.TOKEN_PREFIX;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
// Reads the JWT from the Authorization header, and then uses JWT to validate the token
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if (user != null) {
// new arraylist means authorities
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
package cz.muni.fi.pa165.icehockeymanager.rest.security;
public class SecurityConstants {
public static final String SECRET = "SECRET_KEY";
public static final long EXPIRATION_TIME = 900_000; // 15 mins
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
public static final String SIGN_UP_URL = "/pa165/rest/auth/signup";
public static final String AUTH_URL = "/pa165/rest/auth/verify";
}
package cz.muni.fi.pa165.icehockeymanager.rest.security;
import cz.muni.fi.pa165.icehockeymanager.facades.UserAuthFacade;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import static cz.muni.fi.pa165.icehockeymanager.rest.security.SecurityConstants.SIGN_UP_URL;
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private final UserAuthFacade userAuthFacade;
public WebSecurity(UserAuthFacade userAuthFacade) {
this.userAuthFacade = userAuthFacade;
}
@Override
@Order(1)
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(userAuthFacade))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration().applyPermitDefaultValues();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}
......@@ -98,6 +98,12 @@
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.16.0</version>
</dependency>
</dependencies>
......
......@@ -4,6 +4,7 @@ import cz.muni.fi.pa165.icehockeymanager.dto.TeamDto;
import cz.muni.fi.pa165.icehockeymanager.dto.UserDto;
import cz.muni.fi.pa165.icehockeymanager.exceptions.UnknownTeamException;
import cz.muni.fi.pa165.icehockeymanager.exceptions.UnknownUserException;
import cz.muni.fi.pa165.icehockeymanager.model.Account;
import cz.muni.fi.pa165.icehockeymanager.security.Roles;
import cz.muni.fi.pa165.icehockeymanager.services.BeanMappingService;
import cz.muni.fi.pa165.icehockeymanager.services.TeamService;
......@@ -48,7 +49,7 @@ public class UserAuthFacadeImpl implements UserAuthFacade {
public UserDto createTeamManager(String username, String password, int teamId) {
var team = teamService.findTeam(teamId)
.orElseThrow(() -> new UnknownTeamException(Integer.toString(teamId)));
var user = authService.createTeamManager(username, password);
Account user = authService.createTeamManager(username, password);
teamService.updateTeamManager(team, user);
return beanMapper.mapTo(user, UserDto.class);
}
......
......@@ -5,7 +5,7 @@ import com.github.dozermapper.core.Mapper;
import cz.muni.fi.pa165.icehockeymanager.dto.GameDto;
import cz.muni.fi.pa165.icehockeymanager.dto.UserDto;
import cz.muni.fi.pa165.icehockeymanager.model.Game;
import cz.muni.fi.pa165.icehockeymanager.model.User;
import cz.muni.fi.pa165.icehockeymanager.model.Account;
import java.util.Collection;
import java.util.List;
......@@ -44,7 +44,7 @@ public interface BeanMappingService {
GameDto mapTo(Game u, Class<GameDto> mapToClass);
UserDto mapTo(User u, Class<UserDto> mapToClass);
UserDto mapTo(Account u, Class<UserDto> mapToClass);
List<GameDto> mapCollection(Collection<Game> objects, Class<GameDto> mapToClass);
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment