diff --git a/.gitignore b/.gitignore
index 6c0de1a..cd85925 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ target/
!**/src/test/**/target/
/config.json
resources/*
+run
### STS ###
.apt_generated
diff --git a/pom.xml b/pom.xml
index 5700481..5c36dbe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -113,6 +113,26 @@
compile
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.2
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.2
+ runtime
+
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.2
+ runtime
+
+
diff --git a/src/main/java/com/imjustdoom/pluginsite/config/custom/JwtConfig.java b/src/main/java/com/imjustdoom/pluginsite/config/custom/JwtConfig.java
new file mode 100644
index 0000000..83e5fe6
--- /dev/null
+++ b/src/main/java/com/imjustdoom/pluginsite/config/custom/JwtConfig.java
@@ -0,0 +1,44 @@
+package com.imjustdoom.pluginsite.config.custom;
+
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import lombok.Getter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.ConstructorBinding;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.Key;
+import java.time.Duration;
+
+@Getter
+@ConfigurationProperties("jwt")
+@ConstructorBinding
+public class JwtConfig {
+ private static final Path KEY_PATH = Path.of("./jwt_secret");
+
+ private final Duration expiryTime;
+ private final String domain;
+ private final boolean secureCookie;
+ private final Key key;
+
+ public JwtConfig(Duration expiryTime, String domain, boolean secureCookie, String key) throws IOException {
+ this.expiryTime = expiryTime;
+ this.domain = domain;
+ this.secureCookie = secureCookie;
+ byte[] encodedSecret;
+ if (key == null) {
+ if (Files.exists(KEY_PATH)) {
+ encodedSecret = Files.readAllBytes(KEY_PATH);
+ } else {
+ encodedSecret = Keys.secretKeyFor(SignatureAlgorithm.HS512).getEncoded();
+ Files.write(KEY_PATH, encodedSecret);
+ }
+ } else {
+ encodedSecret = key.getBytes(StandardCharsets.UTF_8);
+ }
+ this.key = Keys.hmacShaKeyFor(encodedSecret);
+ }
+}
diff --git a/src/main/java/com/imjustdoom/pluginsite/config/custom/SiteConfig.java b/src/main/java/com/imjustdoom/pluginsite/config/custom/SiteConfig.java
index cbd476d..48897d5 100644
--- a/src/main/java/com/imjustdoom/pluginsite/config/custom/SiteConfig.java
+++ b/src/main/java/com/imjustdoom/pluginsite/config/custom/SiteConfig.java
@@ -9,7 +9,7 @@
import org.springframework.util.unit.DataUnit;
@Getter
-@ConfigurationProperties(prefix = "app")
+@ConfigurationProperties("app")
@ConstructorBinding
@AllArgsConstructor
public class SiteConfig {
diff --git a/src/main/java/com/imjustdoom/pluginsite/config/security/SecurityConfig.java b/src/main/java/com/imjustdoom/pluginsite/config/security/SecurityConfig.java
index ff26b3f..f256609 100644
--- a/src/main/java/com/imjustdoom/pluginsite/config/security/SecurityConfig.java
+++ b/src/main/java/com/imjustdoom/pluginsite/config/security/SecurityConfig.java
@@ -1,37 +1,74 @@
package com.imjustdoom.pluginsite.config.security;
+import com.imjustdoom.pluginsite.config.custom.SiteConfig;
+import com.imjustdoom.pluginsite.config.security.jwt.JwtAuthenticationFilter;
import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.authentication.AuthenticationManager;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@Configuration
@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
- private final DaoAuthenticationProvider authProvider;
+ private final UserDetailsService userDetailsService;
+ private final PasswordEncoder passwordEncoder;
+
+ private final JwtAuthenticationFilter jwtAuthenticationFilter;
+ private final SiteConfig siteConfig;
+
+ @Bean
+ @Override
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ return super.authenticationManagerBean();
+ }
@Override
- protected void configure(AuthenticationManagerBuilder auth) {
- auth.authenticationProvider(this.authProvider);
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.userDetailsService(this.userDetailsService).passwordEncoder(this.passwordEncoder);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
- .csrf().disable()
+ .cors()
+ .and().csrf()
+ .ignoringAntMatchers("/auth/**")
+ .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
- .authorizeRequests()
- .antMatchers("/admin", "/admin/roles").hasRole("ADMIN")
- .antMatchers("/resources/create", "/account/details").authenticated()
- .antMatchers("/register", "/login").not().authenticated()
+ .and().authorizeRequests().anyRequest().permitAll()
- .anyRequest().permitAll()
- .and()
- .formLogin().loginProcessingUrl("/login");
+ .and().logout().disable()
+ .httpBasic().disable()
+ .formLogin().disable()
+ .exceptionHandling().authenticationEntryPoint((request, response, authException) -> response.sendError(HttpStatus.NOT_FOUND.value()))
+
+ .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+
+ .and().addFilterBefore(this.jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
+
+// @Bean
+// public CorsConfigurationSource corsConfigurationSource() {
+// CorsConfiguration configuration = new CorsConfiguration();
+// configuration.setAllowedOrigins(Collections.singletonList(this.siteConfig.getDomain()));
+// configuration.addAllowedHeader("*");
+// configuration.addAllowedMethod("*");
+// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+// source.registerCorsConfiguration("/**", configuration);
+// return source;
+// }
}
\ No newline at end of file
diff --git a/src/main/java/com/imjustdoom/pluginsite/config/security/jwt/JwtAuthenticationFilter.java b/src/main/java/com/imjustdoom/pluginsite/config/security/jwt/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..bf67910
--- /dev/null
+++ b/src/main/java/com/imjustdoom/pluginsite/config/security/jwt/JwtAuthenticationFilter.java
@@ -0,0 +1,55 @@
+package com.imjustdoom.pluginsite.config.security.jwt;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@RequiredArgsConstructor
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+ private final JwtProvider jwtProvider;
+ private final UserDetailsService userDetailsService;
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ String jwt = this.getJwtToken(request);
+ System.out.println("Doing filter A " + jwt);
+ if (jwt != null && !jwt.isEmpty() && this.jwtProvider.validateToken(jwt)) {
+ System.out.println("Doing filter B");
+ String username = this.jwtProvider.getSubjectFromToken(jwt);
+ System.out.println("Doing filter B " + username);
+ UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
+ System.out.println("Doing filter C " + userDetails);
+ UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+ System.out.println("Doing filter D " + authentication);
+ authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ System.out.println("Set authentication");
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ private String getJwtToken(HttpServletRequest request) {
+ if (request.getCookies() == null)
+ return null;
+ for (Cookie cookie : request.getCookies()) {
+ if (!cookie.getName().equals(JwtProvider.COOKIE_NAME))
+ continue;
+ return cookie.getValue();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/imjustdoom/pluginsite/config/security/jwt/JwtProvider.java b/src/main/java/com/imjustdoom/pluginsite/config/security/jwt/JwtProvider.java
new file mode 100644
index 0000000..197e581
--- /dev/null
+++ b/src/main/java/com/imjustdoom/pluginsite/config/security/jwt/JwtProvider.java
@@ -0,0 +1,71 @@
+package com.imjustdoom.pluginsite.config.security.jwt;
+
+import com.imjustdoom.pluginsite.config.custom.JwtConfig;
+import com.imjustdoom.pluginsite.model.Account;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.UnsupportedJwtException;
+import io.jsonwebtoken.security.SignatureException;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.Cookie;
+import java.time.Instant;
+import java.util.Date;
+
+@Component
+@RequiredArgsConstructor
+public class JwtProvider {
+ public static final String COOKIE_NAME = "JWT-TOKEN";
+ private static final String USER_ID = "userId";
+
+ private final JwtConfig jwtConfig;
+
+ public Cookie generateTokenCookie(Authentication authentication) {
+ Cookie cookie = new Cookie(COOKIE_NAME, this.generateToken(authentication));
+ cookie.setSecure(this.jwtConfig.isSecureCookie());
+ cookie.setHttpOnly(true);
+ cookie.setMaxAge((int) this.jwtConfig.getExpiryTime().getSeconds());
+ cookie.setPath("/");
+ if (this.jwtConfig.getDomain() != null)
+ cookie.setDomain(this.jwtConfig.getDomain());
+
+ return cookie;
+ }
+
+ public String generateToken(Authentication authentication) {
+ Account account = (Account) authentication.getPrincipal();
+
+ Claims claims = Jwts.claims().setSubject(account.getUsername());
+ claims.put(USER_ID, account.getId());
+
+ return Jwts.builder()
+ .setClaims(claims)
+ .setIssuedAt(Date.from(Instant.now()))
+ .setExpiration(Date.from(Instant.now().plus(this.jwtConfig.getExpiryTime())))
+ .signWith(this.jwtConfig.getKey())
+ .compact();
+ }
+
+ public String getSubjectFromToken(String token) {
+ return Jwts.parserBuilder()
+ .setSigningKey(this.jwtConfig.getKey())
+ .build()
+ .parseClaimsJws(token)
+ .getBody().getSubject();
+ }
+
+ public boolean validateToken(String token) {
+ try {
+ return Jwts.parserBuilder()
+ .setSigningKey(this.jwtConfig.getKey())
+ .build()
+ .parseClaimsJws(token) != null;
+ } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException ex) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/imjustdoom/pluginsite/controller/AccountController.java b/src/main/java/com/imjustdoom/pluginsite/controller/AccountController.java
index 882e50b..9344a42 100644
--- a/src/main/java/com/imjustdoom/pluginsite/controller/AccountController.java
+++ b/src/main/java/com/imjustdoom/pluginsite/controller/AccountController.java
@@ -2,7 +2,6 @@
import com.imjustdoom.pluginsite.config.exception.RestErrorCode;
import com.imjustdoom.pluginsite.config.exception.RestException;
-import com.imjustdoom.pluginsite.dtos.in.account.CreateAccountRequest;
import com.imjustdoom.pluginsite.dtos.in.account.UpdateAccountRequest;
import com.imjustdoom.pluginsite.dtos.out.account.AccountDto;
import com.imjustdoom.pluginsite.dtos.out.account.SelfAccountDto;
@@ -14,10 +13,10 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
-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.RequestParam;
@@ -31,21 +30,14 @@ public class AccountController {
private final AccountService accountService;
private final MessageService messageService;
- @PostMapping("/register")
- public String signupSubmit(@RequestBody CreateAccountRequest accountRequest) throws RestException {
- Account account = this.accountService.register(accountRequest);
-
- return ""; // todo return a registration DTO of some sort, probably with a session token
- }
-
- // todo login with JWT
-
@GetMapping("/details")
+ @PreAuthorize("isAuthenticated()")
public SelfAccountDto getSelfAccountDetails(Account account) {
return SelfAccountDto.fromAccount(account);
}
@PatchMapping("/details")
+ @PreAuthorize("isAuthenticated()")
public SelfAccountDto updateAccountDetails(Account account, @RequestBody UpdateAccountRequest request,
@RequestParam(value = "profilePicture", required = false) MultipartFile file) throws RestException {
return SelfAccountDto.fromAccount(this.accountService.updateAccountDetails(account, request, file));
@@ -57,6 +49,7 @@ public AccountDto getAccountDetails(@PathVariable int id) throws RestException {
}
@GetMapping("/groups")
+ @PreAuthorize("isAuthenticated()")
public Page getMessageGroups(Account account,
@PageableDefault(size = 25) Pageable pageable) throws RestException {
if (pageable.getPageSize() > 50) throw new RestException(RestErrorCode.PAGE_SIZE_TOO_LARGE, "Page size is too large (%s > %s)", pageable.getPageSize(), 50);
diff --git a/src/main/java/com/imjustdoom/pluginsite/controller/AdminController.java b/src/main/java/com/imjustdoom/pluginsite/controller/AdminController.java
index c20a8e6..ce5061f 100644
--- a/src/main/java/com/imjustdoom/pluginsite/controller/AdminController.java
+++ b/src/main/java/com/imjustdoom/pluginsite/controller/AdminController.java
@@ -9,6 +9,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -19,6 +20,7 @@
@RestController
@RequestMapping("/admin")
@RequiredArgsConstructor
+@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public class AdminController {
private final ReportService reportService;
diff --git a/src/main/java/com/imjustdoom/pluginsite/controller/AuthenticationController.java b/src/main/java/com/imjustdoom/pluginsite/controller/AuthenticationController.java
new file mode 100644
index 0000000..fdfb95a
--- /dev/null
+++ b/src/main/java/com/imjustdoom/pluginsite/controller/AuthenticationController.java
@@ -0,0 +1,47 @@
+package com.imjustdoom.pluginsite.controller;
+
+import com.imjustdoom.pluginsite.config.exception.RestException;
+import com.imjustdoom.pluginsite.config.security.jwt.JwtProvider;
+import com.imjustdoom.pluginsite.dtos.in.LoginRequest;
+import com.imjustdoom.pluginsite.dtos.in.account.CreateAccountRequest;
+import com.imjustdoom.pluginsite.model.Account;
+import com.imjustdoom.pluginsite.service.AccountService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+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;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+@RestController
+@RequestMapping("/auth")
+@RequiredArgsConstructor
+public class AuthenticationController {
+ private final AuthenticationManager authenticationManager;
+ private final AccountService accountService;
+ private final JwtProvider jwtProvider;
+
+ @PostMapping("/register")
+ public String register(@RequestBody CreateAccountRequest accountRequest) throws RestException {
+ Account account = this.accountService.register(accountRequest);
+
+ return ""; // todo return a registration DTO of some sort, probably with a session token
+ }
+
+ @PostMapping("/login")
+ public void login(@RequestBody LoginRequest request, HttpServletResponse response) {
+ Authentication authentication = this.authenticationManager
+ .authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+
+ Cookie cookie = this.jwtProvider.generateTokenCookie(authentication);
+ response.addCookie(cookie);
+ }
+}
diff --git a/src/main/java/com/imjustdoom/pluginsite/controller/MessageController.java b/src/main/java/com/imjustdoom/pluginsite/controller/MessageController.java
index 05c835a..8c275b9 100644
--- a/src/main/java/com/imjustdoom/pluginsite/controller/MessageController.java
+++ b/src/main/java/com/imjustdoom/pluginsite/controller/MessageController.java
@@ -4,6 +4,7 @@
import com.imjustdoom.pluginsite.model.Account;
import com.imjustdoom.pluginsite.service.MessageService;
import lombok.RequiredArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@@ -16,6 +17,7 @@ public class MessageController {
private final MessageService messageService;
@PostMapping
+ @PreAuthorize("isAuthenticated()")
public void sendMessage(Account account, @RequestParam int groupId, @RequestParam String content) throws RestException {
this.messageService.sendMessage(account, groupId, content);
}
diff --git a/src/main/java/com/imjustdoom/pluginsite/controller/MessageGroupController.java b/src/main/java/com/imjustdoom/pluginsite/controller/MessageGroupController.java
index 78058b6..05515da 100644
--- a/src/main/java/com/imjustdoom/pluginsite/controller/MessageGroupController.java
+++ b/src/main/java/com/imjustdoom/pluginsite/controller/MessageGroupController.java
@@ -9,6 +9,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
@@ -25,37 +26,44 @@ public class MessageGroupController {
private final MessageService messageService;
@PostMapping
+ @PreAuthorize("isAuthenticated()")
public void createGroup(Account sender, @RequestParam int targetId) throws RestException {
this.messageService.createGroup(sender, targetId);
}
@GetMapping("/{groupId}")
+ @PreAuthorize("isAuthenticated()")
public MessageGroupDto getGroup(Account account, @PathVariable int groupId) throws RestException {
return MessageGroupDto.fromMessageGroup(this.messageService.getGroup(account, groupId));
}
@GetMapping("/{groupId}/messages")
+ @PreAuthorize("isAuthenticated()")
public Page getMessages(Account account, @PathVariable int groupId, @PageableDefault(size = 25) Pageable pageable) throws RestException {
return this.messageService.getMessages(account, groupId, pageable)
.map(MessageDto::fromMessage);
}
@DeleteMapping("/{groupId}")
+ @PreAuthorize("isAuthenticated()")
public void deleteGroup(Account account, @PathVariable int groupId) throws RestException {
this.messageService.deleteGroup(account, groupId);
}
@PostMapping("/{groupId}/addUser")
+ @PreAuthorize("isAuthenticated()")
public void addUserToGroup(Account account, @PathVariable int groupId, @RequestParam int targetId) throws RestException {
this.messageService.addUserToGroup(account, groupId, targetId);
}
@PostMapping("/{groupId}/leave")
+ @PreAuthorize("isAuthenticated()")
public void leaveGroup(Account account, @PathVariable int groupId) throws RestException {
this.messageService.leaveGroup(account, groupId);
}
@PatchMapping("/{groupId}/name")
+ @PreAuthorize("isAuthenticated()")
public MessageGroupDto editGroupName(Account account, @PathVariable int groupId, @RequestParam String name) throws RestException {
return MessageGroupDto.fromMessageGroup(this.messageService.editGroupName(account, groupId, name));
}
diff --git a/src/main/java/com/imjustdoom/pluginsite/controller/ReportController.java b/src/main/java/com/imjustdoom/pluginsite/controller/ReportController.java
index 0e27017..95b9336 100644
--- a/src/main/java/com/imjustdoom/pluginsite/controller/ReportController.java
+++ b/src/main/java/com/imjustdoom/pluginsite/controller/ReportController.java
@@ -4,6 +4,7 @@
import com.imjustdoom.pluginsite.model.Account;
import com.imjustdoom.pluginsite.service.ReportService;
import lombok.RequiredArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -16,6 +17,7 @@ public class ReportController {
private final ReportService reportService;
@PostMapping
+ @PreAuthorize("isAuthenticated()")
public void createReport(Account account, @RequestBody CreateReportRequest request) {
this.reportService.createReport(account, request);
}
diff --git a/src/main/java/com/imjustdoom/pluginsite/controller/ResourceController.java b/src/main/java/com/imjustdoom/pluginsite/controller/ResourceController.java
index fab06d0..dac2787 100644
--- a/src/main/java/com/imjustdoom/pluginsite/controller/ResourceController.java
+++ b/src/main/java/com/imjustdoom/pluginsite/controller/ResourceController.java
@@ -15,6 +15,7 @@
import org.springframework.data.domain.Sort;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.data.web.PageableDefault;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -40,16 +41,19 @@ public Page searchResources(@PageableDefault(size = 25, sort
@PostMapping
public void createResource(Account account, CreateResourceRequest request) throws RestException {
+ System.out.println("Account " + account + " request: " + request);
this.resourceService.createResource(account, request);
}
@PostMapping("/{resourceId}/edit")
+ @PreAuthorize("isAuthenticated()")
public void updateResourceInfo(Account account, @PathVariable int resourceId, @RequestParam(value = "file", required = false) MultipartFile file, CreateResourceRequest request) throws RestException {
this.resourceService.updateResource(account, resourceId, file, request);
}
// todo properly delete
@DeleteMapping("/{resourceId}")
+ @PreAuthorize("isAuthenticated()")
public void deleteResource(@PathVariable int resourceId) {
this.resourceRepository.updateStatusById(resourceId, "removed");
}
diff --git a/src/main/java/com/imjustdoom/pluginsite/controller/ResourceUpdateController.java b/src/main/java/com/imjustdoom/pluginsite/controller/ResourceUpdateController.java
index 385bfcd..906e994 100644
--- a/src/main/java/com/imjustdoom/pluginsite/controller/ResourceUpdateController.java
+++ b/src/main/java/com/imjustdoom/pluginsite/controller/ResourceUpdateController.java
@@ -11,6 +11,7 @@
import org.springframework.http.ContentDisposition;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -30,16 +31,19 @@ public class ResourceUpdateController {
private final ResourceUpdateService resourceUpdateService;
@PatchMapping("/{updateId}/status")
+ @PreAuthorize("isAuthenticated()")
public Update changeStatus(Account account, @PathVariable int updateId, @RequestParam String status) throws RestException {
return this.resourceUpdateService.changeStatus(account, updateId, status);
}
@PatchMapping("/{updateId}")
+ @PreAuthorize("isAuthenticated()")
public Update editUpdate(Account account, @PathVariable int updateId, @RequestBody EditResourceUpdateRequest request) throws RestException {
return this.resourceUpdateService.editUpdate(account, updateId, request);
}
- @PostMapping("/")
+ @PostMapping
+ @PreAuthorize("isAuthenticated()")
public void createUpdate(Account account, @RequestParam int resourceId,
@RequestParam MultipartFile file, @RequestBody CreateUpdateRequest updateRequest) throws RestException {
this.resourceUpdateService.createUpdate(account, resourceId, file, updateRequest);
diff --git a/src/main/java/com/imjustdoom/pluginsite/dtos/in/LoginRequest.java b/src/main/java/com/imjustdoom/pluginsite/dtos/in/LoginRequest.java
new file mode 100644
index 0000000..cccdffe
--- /dev/null
+++ b/src/main/java/com/imjustdoom/pluginsite/dtos/in/LoginRequest.java
@@ -0,0 +1,13 @@
+package com.imjustdoom.pluginsite.dtos.in;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Setter
+@Getter
+@NoArgsConstructor
+public class LoginRequest {
+ private String username;
+ private String password;
+}
diff --git a/src/main/java/com/imjustdoom/pluginsite/dtos/out/ProfileDto.java b/src/main/java/com/imjustdoom/pluginsite/dtos/out/ProfileDto.java
index fed62a7..067e66a 100644
--- a/src/main/java/com/imjustdoom/pluginsite/dtos/out/ProfileDto.java
+++ b/src/main/java/com/imjustdoom/pluginsite/dtos/out/ProfileDto.java
@@ -3,6 +3,6 @@
import com.imjustdoom.pluginsite.dtos.out.account.AccountDto;
public record ProfileDto(int id, int totalDownloads,
- AccountDto accountDto) {
+ AccountDto account) {
}
diff --git a/src/main/java/com/imjustdoom/pluginsite/model/Account.java b/src/main/java/com/imjustdoom/pluginsite/model/Account.java
index d0be555..e125fee 100644
--- a/src/main/java/com/imjustdoom/pluginsite/model/Account.java
+++ b/src/main/java/com/imjustdoom/pluginsite/model/Account.java
@@ -7,10 +7,21 @@
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
-import javax.persistence.*;
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToMany;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
@Setter
@@ -24,7 +35,7 @@ public Account(String username, String email, String password) {
this.email = email;
this.password = password;
this.joined = LocalDateTime.now();
- this.role = "ROLE_USER";
+ this.role = Role.USER;
}
@Id
@@ -52,7 +63,8 @@ public Account(String username, String email, String password) {
private int totalDownloads;
@Column(nullable = false)
- private String role;
+ @Enumerated(EnumType.STRING)
+ private Role role;
@ManyToMany(mappedBy = "members")
private List groups;
@@ -60,17 +72,13 @@ public Account(String username, String email, String password) {
@Lob
@Column(name = "profile_picture")
private byte[] profilePicture;
-
+
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "author")
private List messages = new ArrayList<>();
@Override
public Collection extends GrantedAuthority> getAuthorities() {
- List list = new ArrayList<>();
-
- list.add(new SimpleGrantedAuthority(getRole()));
-
- return list;
+ return Collections.singleton(new SimpleGrantedAuthority(this.role.toString()));
}
@Override
@@ -92,4 +100,14 @@ public boolean isCredentialsNonExpired() {
public boolean isEnabled() {
return true;
}
+
+ public enum Role {
+ USER,
+ ADMIN;
+
+ @Override
+ public String toString() {
+ return "ROLE_".concat(super.toString());
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index c31a3b4..a82ad1c 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -15,3 +15,7 @@ app.domain=http://localhost:8080
app.max-updates-per-hour=10
app.max-creations-per-hour=2
app.max-upload-size=10MB
+
+jwt.expiry-time=30m
+jwt.domain=localhost
+jwt.secure-cookie=false