diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/SecurityConfig.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/SecurityConfig.java index 59cf65f..6d016d8 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/SecurityConfig.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/SecurityConfig.java @@ -2,6 +2,7 @@ package nl.andrewlalis.gymboard_api.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -9,6 +10,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @Configuration @EnableWebSecurity @@ -20,17 +24,44 @@ public class SecurityConfig { this.tokenAuthenticationFilter = tokenAuthenticationFilter; } + /** + * Defines the security configuration we'll use for this API. + * @param http The security configurable. + * @return The filter chain to apply. + * @throws Exception If an error occurs while configuring. + */ @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .httpBasic().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .csrf().disable() + .cors().and() .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .authorizeHttpRequests().anyRequest().permitAll(); return http.build(); } + /** + * Defines the CORS configuration for this API, which is to say that we + * allow cross-origin requests ONLY from the web app for the vast majority + * of endpoints. + * @return The CORS configuration source. + */ + @Bean + @Order(1) + public CorsConfigurationSource corsConfigurationSource() { + final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + final CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // Don't do this in production, use a proper list of allowed origins + config.addAllowedOriginPattern("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + source.registerCorsConfiguration("/**", config); + return source; + } + @Bean public AuthenticationManager authenticationManager() { return null;// Disable the standard spring authentication manager. diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/WebConfig.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/WebConfig.java index e1bbd40..f78f351 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/WebConfig.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/WebConfig.java @@ -1,31 +1,11 @@ package nl.andrewlalis.gymboard_api.config; import nl.andrewlalis.gymboard_api.util.ULID; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -import java.time.OffsetDateTime; -import java.util.Arrays; @Configuration public class WebConfig { - @Bean - public CorsFilter corsFilter() { - final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - final CorsConfiguration config = new CorsConfiguration(); - config.setAllowCredentials(true); - // Don't do this in production, use a proper list of allowed origins - config.addAllowedOriginPattern("*"); - config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept")); - config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")); - source.registerCorsConfiguration("/**", config); - return new CorsFilter(source); - } - @Bean public ULID ulid() { return new ULID(); diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/AuthController.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/AuthController.java index 52901e0..46b8e43 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/AuthController.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/AuthController.java @@ -2,7 +2,12 @@ package nl.andrewlalis.gymboard_api.controller; import nl.andrewlalis.gymboard_api.controller.dto.TokenCredentials; import nl.andrewlalis.gymboard_api.controller.dto.TokenResponse; +import nl.andrewlalis.gymboard_api.controller.dto.UserResponse; +import nl.andrewlalis.gymboard_api.model.auth.User; import nl.andrewlalis.gymboard_api.service.auth.TokenService; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -19,4 +24,10 @@ public class AuthController { public TokenResponse getToken(@RequestBody TokenCredentials credentials) { return tokenService.generateAccessToken(credentials); } + + @GetMapping(path = "/auth/me") + @PreAuthorize("isAuthenticated()") + public UserResponse getMyUser(@AuthenticationPrincipal User user) { + return new UserResponse(user); + } } diff --git a/gymboard-app/src/api/main/auth.ts b/gymboard-app/src/api/main/auth.ts new file mode 100644 index 0000000..a21eb80 --- /dev/null +++ b/gymboard-app/src/api/main/auth.ts @@ -0,0 +1,30 @@ +import { api } from 'src/api/main/index'; + +export interface User { + id: string; + activated: boolean; + email: string; + name: string; +} + +export interface TokenCredentials { + email: string; + password: string; +} + +class AuthModule { + public async getToken(credentials: TokenCredentials): Promise { + const response = await api.post('/auth/token', credentials); + return response.data.token; + } + public async getMyUser(token: string): Promise { + const response = await api.get('/auth/me', { + headers: { + 'Authorization': 'Bearer ' + token + } + }); + return response.data; + } +} + +export default AuthModule; diff --git a/gymboard-app/src/api/main/index.ts b/gymboard-app/src/api/main/index.ts index d8c4158..2255b8d 100644 --- a/gymboard-app/src/api/main/index.ts +++ b/gymboard-app/src/api/main/index.ts @@ -2,6 +2,7 @@ import axios from 'axios'; import GymsModule from 'src/api/main/gyms'; import ExercisesModule from 'src/api/main/exercises'; import LeaderboardsModule from 'src/api/main/leaderboards'; +import AuthModule from 'src/api/main/auth'; export const BASE_URL = 'http://localhost:8080'; @@ -11,6 +12,7 @@ export const api = axios.create({ }); class GymboardApi { + public readonly auth = new AuthModule(); public readonly gyms = new GymsModule(); public readonly exercises = new ExercisesModule(); public readonly leaderboards = new LeaderboardsModule(); diff --git a/gymboard-app/src/layouts/MainLayout.vue b/gymboard-app/src/layouts/MainLayout.vue index 6864950..d842419 100644 --- a/gymboard-app/src/layouts/MainLayout.vue +++ b/gymboard-app/src/layouts/MainLayout.vue @@ -41,8 +41,9 @@ {{ $t('mainLayout.pages') }} - Gyms + Gyms Global Leaderboard + Testing Page diff --git a/gymboard-app/src/pages/TestingPage.vue b/gymboard-app/src/pages/TestingPage.vue new file mode 100644 index 0000000..b9eca7f --- /dev/null +++ b/gymboard-app/src/pages/TestingPage.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/gymboard-app/src/router/routes.ts b/gymboard-app/src/router/routes.ts index b77e582..d11c0e2 100644 --- a/gymboard-app/src/router/routes.ts +++ b/gymboard-app/src/router/routes.ts @@ -5,6 +5,7 @@ import GymPage from 'pages/gym/GymPage.vue'; import GymSubmissionPage from 'pages/gym/GymSubmissionPage.vue'; import GymHomePage from 'pages/gym/GymHomePage.vue'; import GymLeaderboardsPage from 'pages/gym/GymLeaderboardsPage.vue'; +import TestingPage from 'pages/TestingPage.vue'; const routes: RouteRecordRaw[] = [ { @@ -12,6 +13,7 @@ const routes: RouteRecordRaw[] = [ component: MainLayout, children: [ { path: '', component: IndexPage }, + { path: 'testing', component: TestingPage }, { path: 'gyms/:countryCode/:cityShortName/:gymShortName', component: GymPage,