From c57033b35b01141d5656146e33789c9a1feabc16 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Sun, 10 Dec 2023 18:15:00 +0100 Subject: [PATCH 01/14] Add security configuration --- backend/pom.xml | 4 ++ .../security/SecurityConfig.java | 48 +++++++++++++++++++ .../src/main/resources/application.properties | 6 ++- 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java diff --git a/backend/pom.xml b/backend/pom.xml index 43eeb21..afcbe81 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -33,6 +33,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-oauth2-client + org.projectlombok diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java new file mode 100644 index 0000000..44b97b1 --- /dev/null +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java @@ -0,0 +1,48 @@ +package com.github.iskrendev.insuranceprogram.security; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Value("${myapp.environment}") + private String environment; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(a -> a + .anyRequest().permitAll() + ) + .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)) + .logout(l -> { + if (environment.equals("prod")) { + l.logoutSuccessUrl("/").permitAll(); + } else { + l.logoutSuccessUrl("http://localhost:5173").permitAll(); + } + }) + .oauth2Login(o -> { + try { + o.init(http); + if (environment.equals("prod")) { + o.defaultSuccessUrl("/", true); + } else { + o.defaultSuccessUrl("http://localhost:5173"); + } + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + }); + return http.build(); + } +} diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 3250028..071cd8a 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -1,2 +1,6 @@ spring.data.mongodb.uri=${MONGODB_URI} -spring.mvc.hiddenmethod.filter.enabled= true \ No newline at end of file +spring.mvc.hiddenmethod.filter.enabled= true +spring.security.oauth2.client.registration.github.client-id=${GITHUB_CLIENT_ID} +spring.security.oauth2.client.registration.github.client-secret=${GITHUB_CLIENT_SECRET} +spring.security.oauth2.client.registration.github.scope=none +myapp.environment=local \ No newline at end of file From 8b63fcd44f9fa40d42385da33942b0802b8951e7 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Mon, 11 Dec 2023 03:19:36 +0100 Subject: [PATCH 02/14] Add LoginPage, AuthController, change routing paths and application.properties --- .../security/AuthController.java | 21 +++++++++++++++ .../security/SecurityConfig.java | 6 ++--- .../src/main/resources/application.properties | 6 ++--- frontend/src/App.tsx | 6 +++-- frontend/src/components/header/Header.tsx | 26 ++++++++++++------- frontend/src/pages/AddPage.tsx | 2 +- frontend/src/pages/EditPage.tsx | 4 +-- frontend/src/pages/LoginPage.css | 8 ++++++ frontend/src/pages/LoginPage.tsx | 17 ++++++++++++ 9 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java create mode 100644 frontend/src/pages/LoginPage.css create mode 100644 frontend/src/pages/LoginPage.tsx diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java new file mode 100644 index 0000000..d856a8f --- /dev/null +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java @@ -0,0 +1,21 @@ +package com.github.iskrendev.insuranceprogram.security; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/auth") +public class AuthController { + + @GetMapping("/me") + public String getMe() { + var principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if (principal instanceof DefaultOAuth2User defaultOAuth2User) { + return defaultOAuth2User.getAttributes().get("login").toString(); + } + return "anonymous"; + } +} diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java index 44b97b1..66cfd00 100644 --- a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java @@ -35,12 +35,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti try { o.init(http); if (environment.equals("prod")) { - o.defaultSuccessUrl("/", true); + o.defaultSuccessUrl("/home", true); } else { - o.defaultSuccessUrl("http://localhost:5173"); + o.defaultSuccessUrl("http://localhost:5173/home", true); } } catch (Exception e) { - throw new IllegalArgumentException(e); + throw new RuntimeException(e); } }); return http.build(); diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 071cd8a..d41ef75 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -1,6 +1,6 @@ spring.data.mongodb.uri=${MONGODB_URI} spring.mvc.hiddenmethod.filter.enabled= true -spring.security.oauth2.client.registration.github.client-id=${GITHUB_CLIENT_ID} -spring.security.oauth2.client.registration.github.client-secret=${GITHUB_CLIENT_SECRET} +spring.security.oauth2.client.registration.github.client-id=${GITHUB_ID} +spring.security.oauth2.client.registration.github.client-secret=${GITHUB_SECRET} spring.security.oauth2.client.registration.github.scope=none -myapp.environment=local \ No newline at end of file +myapp.environment=${ENVIRONMENT} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c539004..f12ca6c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,8 +1,9 @@ import Header from "./components/header/Header.tsx"; import './App.css' import {Route, Routes} from "react-router-dom"; -import AddPage from './pages/AddPage.tsx'; +import LoginPage from './pages/LoginPage.tsx'; import HomePage from './pages/HomePage.tsx'; +import AddPage from './pages/AddPage.tsx'; import DetailsPage from "./pages/DetailsPage.tsx"; import EditPage from "./pages/EditPage.tsx"; @@ -12,7 +13,8 @@ function App() { <>
- } /> + } /> + } /> } /> } /> } /> diff --git a/frontend/src/components/header/Header.tsx b/frontend/src/components/header/Header.tsx index fb33b27..6a5f769 100644 --- a/frontend/src/components/header/Header.tsx +++ b/frontend/src/components/header/Header.tsx @@ -1,17 +1,23 @@ import "./Header.css"; import HomeIcon from "../svg/HomeIcon.tsx"; -import { Link } from "react-router-dom"; +import {Link, useLocation} from "react-router-dom"; function Header() { - return ( -
- -
- ); + + const location = useLocation(); + const isLoginPage = location.pathname === '/login'; + + if (!isLoginPage) { + return ( +
+ +
+ ); + } } export default Header; \ No newline at end of file diff --git a/frontend/src/pages/AddPage.tsx b/frontend/src/pages/AddPage.tsx index 11e04af..fda7f0c 100644 --- a/frontend/src/pages/AddPage.tsx +++ b/frontend/src/pages/AddPage.tsx @@ -75,7 +75,7 @@ function AddPage() { axios.post(saveEndpoint, newInsuranceData) .then(() => { - navigate('/') + navigate('/home') }) .catch((error: AxiosError) => { console.error('Error adding data:', error); diff --git a/frontend/src/pages/EditPage.tsx b/frontend/src/pages/EditPage.tsx index 369a55b..8843f72 100644 --- a/frontend/src/pages/EditPage.tsx +++ b/frontend/src/pages/EditPage.tsx @@ -58,7 +58,7 @@ function EditPage() { const editedInsuranceData = {...insuranceData, type}; axios .put(`/api/${type}/${id}`, editedInsuranceData) - .then(() => navigate("/")) + .then(() => navigate("/home")) .catch(error => { console.error('Error updating insurance data:', error); }); @@ -81,7 +81,7 @@ function EditPage() { axios .delete(`/api/${type}/${id}`) .then(() => { - navigate("/"); + navigate("/home"); }) .catch((error) => { console.error('Error deleting insurance data:', error); diff --git a/frontend/src/pages/LoginPage.css b/frontend/src/pages/LoginPage.css new file mode 100644 index 0000000..8a13f92 --- /dev/null +++ b/frontend/src/pages/LoginPage.css @@ -0,0 +1,8 @@ +.button-login { + width: 250px; + height: 40px; + position: absolute; + transform: translateX(-50%); + color: #f5f5f5; + box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx new file mode 100644 index 0000000..26ef8f5 --- /dev/null +++ b/frontend/src/pages/LoginPage.tsx @@ -0,0 +1,17 @@ +import "./LoginPage.css"; + +function LoginPage() { + function login() { + const host = window.location.host === 'localhost:5173' ? 'http://localhost:8080' : window.location.origin + + window.open(host + '/oauth2/authorization/github', '_self') + } + + return ( + <> + + + ) +} + +export default LoginPage; \ No newline at end of file From 8f244047260877a006b7be417db7ae0d7d1b2b31 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Tue, 12 Dec 2023 04:45:09 +0100 Subject: [PATCH 03/14] Add AppUser record, refactor AuthController and LoginPage, change CSS organization, add some styling and responsive design --- .../insuranceprogram/security/AppUser.java | 14 +++ .../security/AuthController.java | 9 +- .../src/components/content/InsuranceList.tsx | 11 +- frontend/src/components/header/Header.css | 19 ++++ frontend/src/components/header/Header.tsx | 13 ++- frontend/src/components/svg/LogoutIcon.tsx | 15 +++ .../src/components/svg/StatisticsIcon.tsx | 14 +++ frontend/src/index.css | 5 +- .../src/modals/DeleteConfirmationModal.tsx | 1 + frontend/src/pages/AddPage.css | 74 ------------- frontend/src/pages/AddPage.tsx | 2 +- frontend/src/pages/DetailsPage.tsx | 2 +- frontend/src/pages/EditPage.tsx | 6 +- frontend/src/pages/HomePage.css | 94 ++++++++++++---- frontend/src/pages/HomePage.tsx | 5 +- frontend/src/pages/LoginPage.tsx | 19 +++- frontend/src/pages/SharedComponents.css | 103 ++++++++++++++++++ frontend/src/types/types.ts | 5 + 18 files changed, 291 insertions(+), 120 deletions(-) create mode 100644 backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AppUser.java create mode 100644 frontend/src/components/svg/LogoutIcon.tsx create mode 100644 frontend/src/components/svg/StatisticsIcon.tsx create mode 100644 frontend/src/pages/SharedComponents.css diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AppUser.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AppUser.java new file mode 100644 index 0000000..ee141b4 --- /dev/null +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AppUser.java @@ -0,0 +1,14 @@ +package com.github.iskrendev.insuranceprogram.security; + +import lombok.Builder; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Builder +@Document(collection = "users") +public record AppUser( + @Id + int id, + String login +) { +} diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java index d856a8f..5792148 100644 --- a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java @@ -11,11 +11,14 @@ public class AuthController { @GetMapping("/me") - public String getMe() { + public AppUser getMe() { var principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal instanceof DefaultOAuth2User defaultOAuth2User) { - return defaultOAuth2User.getAttributes().get("login").toString(); + return AppUser.builder() + .id(Integer.parseInt(defaultOAuth2User.getAttributes().get("id").toString())) + .login(defaultOAuth2User.getAttributes().get("login").toString()) + .build(); } - return "anonymous"; + throw new IllegalArgumentException("No user logged in"); } } diff --git a/frontend/src/components/content/InsuranceList.tsx b/frontend/src/components/content/InsuranceList.tsx index 52411a6..3fe6830 100644 --- a/frontend/src/components/content/InsuranceList.tsx +++ b/frontend/src/components/content/InsuranceList.tsx @@ -1,6 +1,7 @@ import {InsuranceListProps} from "../../types/types.ts"; import {Link} from "react-router-dom"; import {useState} from "react"; +import "../../pages/SharedComponents.css" function InsuranceList(props: Readonly) { const [sortedInsurances, setSortedInsurances] = useState([...props.insurances]); @@ -27,14 +28,12 @@ function InsuranceList(props: Readonly) { return (
+

{props.headerText}

- + - -

{props.headerText}

{isAccordionOpen && (
    diff --git a/frontend/src/components/header/Header.css b/frontend/src/components/header/Header.css index ab28844..37bd94a 100644 --- a/frontend/src/components/header/Header.css +++ b/frontend/src/components/header/Header.css @@ -9,7 +9,26 @@ } .nav-main { + display: flex; + align-items: center; padding: 8px; text-align: left; margin-left: 30px; + margin-right: 30px; +} + +.home-icon { + margin-right: auto; +} + +.statistics-icon { + margin-right: 25px; +} + +.logout-icon { + margin-right: 5px; +} + +.spacer { + flex-grow: 1; } \ No newline at end of file diff --git a/frontend/src/components/header/Header.tsx b/frontend/src/components/header/Header.tsx index 6a5f769..4da3e89 100644 --- a/frontend/src/components/header/Header.tsx +++ b/frontend/src/components/header/Header.tsx @@ -1,9 +1,10 @@ import "./Header.css"; import HomeIcon from "../svg/HomeIcon.tsx"; -import {Link, useLocation} from "react-router-dom"; +import LogoutIcon from "../svg/LogoutIcon.tsx"; +import { Link, useLocation } from "react-router-dom"; +import StatisticsIcon from "../svg/StatisticsIcon.tsx"; function Header() { - const location = useLocation(); const isLoginPage = location.pathname === '/login'; @@ -12,7 +13,13 @@ function Header() {
    diff --git a/frontend/src/components/svg/LogoutIcon.tsx b/frontend/src/components/svg/LogoutIcon.tsx new file mode 100644 index 0000000..39e3f1b --- /dev/null +++ b/frontend/src/components/svg/LogoutIcon.tsx @@ -0,0 +1,15 @@ +function LogoutIcon() { + return ( + + + + + + + + + + ); +} + +export default LogoutIcon; \ No newline at end of file diff --git a/frontend/src/components/svg/StatisticsIcon.tsx b/frontend/src/components/svg/StatisticsIcon.tsx new file mode 100644 index 0000000..ea7f5f5 --- /dev/null +++ b/frontend/src/components/svg/StatisticsIcon.tsx @@ -0,0 +1,14 @@ +function StatisticsIcon() { + return ( + + + + + + + + + ); +} + +export default StatisticsIcon; \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css index fd880ee..b587848 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -26,7 +26,6 @@ body { margin: 0; padding: 0; display: flex; - place-items: center; min-width: 320px; min-height: 100vh; border: 30px solid; @@ -54,7 +53,11 @@ button { background-color: #1a1a1a; cursor: pointer; transition: border-color 0.25s; + display: flex; + align-items: center; + justify-content: center; } + button:hover { border-color: #ffffff; } diff --git a/frontend/src/modals/DeleteConfirmationModal.tsx b/frontend/src/modals/DeleteConfirmationModal.tsx index 8e8f354..f954635 100644 --- a/frontend/src/modals/DeleteConfirmationModal.tsx +++ b/frontend/src/modals/DeleteConfirmationModal.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import '../modals/DeleteConfirmationModal.css'; import WarningIcon from '../components/svg/WarningIcon.tsx'; interface DeleteConfirmationModalProps { diff --git a/frontend/src/pages/AddPage.css b/frontend/src/pages/AddPage.css index 8509207..fdb6a1b 100644 --- a/frontend/src/pages/AddPage.css +++ b/frontend/src/pages/AddPage.css @@ -1,44 +1,3 @@ -.insurance-form { - display: flex; - margin-top: -25px; -} - -.form-section { - flex: 1; - padding: 70px; - box-sizing: border-box; - text-align: left; - display: flex; - flex-direction: column; - font-weight: bold; -} - -.form-section label { - margin-bottom: 12px; - width: 100%; - display: block; - font-weight: bold; -} - -.form-section label span { - display: inline-block; - width: 150px; - margin-right: 10px; -} - -.form-section input, -.form-section select, -.form-section textarea { - width: calc(100% - 10px); - padding: 10px; - box-sizing: border-box; - margin-bottom: -10px; -} - -.form-section input[name="type"] { - visibility: hidden; -} - .button-container { margin-top: 10px; text-align: center; @@ -54,36 +13,3 @@ color: #f5f5f5; box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); } - -.button-add { - width: 250px; - height: 40px; - position: absolute; - margin-top: 130px; - transform: translateY(-210%); - right: 3%; - box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); -} - -.button-add a { - color: white; -} - -.button-add:hover a { - color: #f5f5f5; - text-decoration: none; -} - -.overview-container { - position: absolute; - top: 100px; - left: 50%; - transform: translateX(-50%); - display: flex; - flex-direction: column; - align-items: center; -} - -.overview-title { - margin-bottom: 60px; -} diff --git a/frontend/src/pages/AddPage.tsx b/frontend/src/pages/AddPage.tsx index fda7f0c..6a1aebf 100644 --- a/frontend/src/pages/AddPage.tsx +++ b/frontend/src/pages/AddPage.tsx @@ -1,11 +1,11 @@ import "./AddPage.css"; +import "./SharedComponents.css"; import React, {useState} from 'react'; import axios, {AxiosError} from "axios"; import {useNavigate} from "react-router-dom"; import FormLabel from "../components/content/FormLabel.tsx"; import moment from "moment"; - function AddPage() { const [firstName, setFirstName] = useState(""); const [familyName, setFamilyName] = useState(""); diff --git a/frontend/src/pages/DetailsPage.tsx b/frontend/src/pages/DetailsPage.tsx index 2833588..94f7b58 100644 --- a/frontend/src/pages/DetailsPage.tsx +++ b/frontend/src/pages/DetailsPage.tsx @@ -1,5 +1,5 @@ -import "./AddPage.css"; import "./DetailsPage.css"; +import "./SharedComponents.css"; import {Link, useParams} from "react-router-dom"; import axios, {AxiosResponse} from "axios"; import {useEffect, useState} from "react"; diff --git a/frontend/src/pages/EditPage.tsx b/frontend/src/pages/EditPage.tsx index 8843f72..2cb7ddc 100644 --- a/frontend/src/pages/EditPage.tsx +++ b/frontend/src/pages/EditPage.tsx @@ -1,5 +1,5 @@ import "./EditPage.css"; -import '../modals/DeleteConfirmationModal.css'; +import "./SharedComponents.css"; import {useEffect, useState} from 'react'; import axios, {AxiosError, AxiosResponse} from "axios"; import {NavigateFunction, useNavigate, useParams} from "react-router-dom"; @@ -99,8 +99,8 @@ function EditPage() { handleClose={handleCloseModal} handleConfirm={handleConfirmDelete} /> -

    Versicherung bearbeiten

    -
    +

    Versicherung bearbeiten

    +
    ([]); @@ -28,7 +26,6 @@ function HomePage() { return ( <> -

    Übersicht

    + { !appUser && } + { + appUser && ( + <> +

    Sie sind als {appUser?.login} angemeldet

    + + ) + } ) } diff --git a/frontend/src/pages/SharedComponents.css b/frontend/src/pages/SharedComponents.css new file mode 100644 index 0000000..c6d6a85 --- /dev/null +++ b/frontend/src/pages/SharedComponents.css @@ -0,0 +1,103 @@ +.insurance-form { + display: flex; + margin-top: -25px; +} + +.form-section { + flex: 1; + padding: 70px; + box-sizing: border-box; + text-align: left; + display: flex; + flex-direction: column; + font-weight: bold; +} + +.form-section label { + margin-bottom: 12px; + width: 100%; + display: block; + font-weight: bold; +} + +.form-section label span { + display: inline-block; + width: 150px; + margin-right: 10px; +} + +.form-section input, +.form-section select, +.form-section textarea { + width: 100%; + padding: 10px; + box-sizing: border-box; + margin-bottom: -10px; +} + +.form-section input[name="type"] { + visibility: hidden; +} + +.overview-container { + display: flex; + flex-direction: column; + align-items: center; +} + +.overview-title { + margin-bottom: 60px; +} + +.accordion-button, +.sort-button { + background-color: transparent; + border: none; + cursor: pointer; + color: white; + width: 32px; + height: 32px; + font-size: 14px; + font-weight: bold; + text-align: right; + box-shadow: 2px 3px 4px rgba(0, 0, 0, 0.1); +} + +.accordion-button { + margin-right: 10px; +} + +.sort-button { + margin-right: -5px; +} + +.asc-icon::before, +.desc-icon::before { + display: inline-block; + vertical-align: middle; + line-height: 1; +} + +.asc-icon::before { + content: '\25B4'; + font-weight: bold; + font-size: 24px; +} + +.desc-icon::before { + content: '\25BE'; + font-weight: bold; + font-size: 24px; +} + +.plus::before { + content: '\002B'; + font-weight: bold; + font-size: 24px; +} + +.minus::before { + content: '\2212'; + font-weight: bold; + font-size: 24px; +} \ No newline at end of file diff --git a/frontend/src/types/types.ts b/frontend/src/types/types.ts index 37a20e5..898bd4a 100644 --- a/frontend/src/types/types.ts +++ b/frontend/src/types/types.ts @@ -59,4 +59,9 @@ export type AllInsurancesResponse = { lifeInsurances: Insurance[]; propertyInsurances: Insurance[]; vehicleInsurances: Insurance[]; +}; + +export type AppUser = { + id: string, + login: string, }; \ No newline at end of file From f9dbce599e7db6f59f9415fbd4ddebeac3ba86dd Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Tue, 12 Dec 2023 16:38:50 +0100 Subject: [PATCH 04/14] Add ProtectedRoutes.tsx, SecuredController and logout function, change SecurityConfig, App.tsx, HomePage.css and Header.tsx and types.ts --- .../securedController/SecuredController.java | 15 +++++++++++++++ .../insuranceprogram/security/SecurityConfig.java | 5 +++++ frontend/src/App.tsx | 10 +++++++++- frontend/src/ProtectedRoutes.tsx | 11 +++++++++++ frontend/src/components/header/Header.tsx | 2 +- frontend/src/pages/HomePage.css | 2 +- frontend/src/pages/LoginPage.tsx | 9 ++++++++- frontend/src/types/types.ts | 4 ++++ 8 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 backend/src/main/java/com/github/iskrendev/insuranceprogram/securedController/SecuredController.java create mode 100644 frontend/src/ProtectedRoutes.tsx diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/securedController/SecuredController.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/securedController/SecuredController.java new file mode 100644 index 0000000..6c8cb69 --- /dev/null +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/securedController/SecuredController.java @@ -0,0 +1,15 @@ +package com.github.iskrendev.insuranceprogram.securedController; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/secured") +public class SecuredController { + @GetMapping + public String secured() { + return "Hello from secured endpoint"; + } + +} diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java index 66cfd00..7f77033 100644 --- a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java @@ -3,11 +3,13 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; 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.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.HttpStatusEntryPoint; @Configuration @EnableWebSecurity @@ -21,9 +23,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(a -> a + .requestMatchers("/api/secured/**").authenticated() .anyRequest().permitAll() ) .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)) + .exceptionHandling(exceptionHandlingConfigurer -> + exceptionHandlingConfigurer.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))) .logout(l -> { if (environment.equals("prod")) { l.logoutSuccessUrl("/").permitAll(); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f12ca6c..352d472 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,11 +1,13 @@ import Header from "./components/header/Header.tsx"; import './App.css' -import {Route, Routes} from "react-router-dom"; +import {Link, Route, Routes} from "react-router-dom"; import LoginPage from './pages/LoginPage.tsx'; import HomePage from './pages/HomePage.tsx'; import AddPage from './pages/AddPage.tsx'; import DetailsPage from "./pages/DetailsPage.tsx"; import EditPage from "./pages/EditPage.tsx"; +import ProtectedRoutes from "./ProtectedRoutes.tsx"; +import {AppUser} from "./types/types.ts"; function App() { @@ -13,7 +15,13 @@ function App() { <>
    + + Secured +
    + )}/> } /> + }/> } /> } /> } /> diff --git a/frontend/src/ProtectedRoutes.tsx b/frontend/src/ProtectedRoutes.tsx new file mode 100644 index 0000000..3a5d4b2 --- /dev/null +++ b/frontend/src/ProtectedRoutes.tsx @@ -0,0 +1,11 @@ +import {ProtectedRoutesProps} from "./types/types.ts"; + + +import {Navigate, Outlet} from "react-router-dom"; +function ProtectedRoutes(props: Readonly) { + const isAuth =props.appUser !== null + + return isAuth ? : +} + +export default ProtectedRoutes \ No newline at end of file diff --git a/frontend/src/components/header/Header.tsx b/frontend/src/components/header/Header.tsx index 4da3e89..63ab723 100644 --- a/frontend/src/components/header/Header.tsx +++ b/frontend/src/components/header/Header.tsx @@ -18,7 +18,7 @@ function Header() { - + diff --git a/frontend/src/pages/HomePage.css b/frontend/src/pages/HomePage.css index 60c252b..4e096f8 100644 --- a/frontend/src/pages/HomePage.css +++ b/frontend/src/pages/HomePage.css @@ -18,7 +18,7 @@ padding: 15px; border: 1px solid #ccc; font-size: 18px; - width: 374px; + width: 400px; max-width: 100%; margin-right: 16px; } diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index c5cde2b..354361b 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -4,13 +4,19 @@ import {useEffect, useState} from "react"; import axios from "axios"; function LoginPage() { - const [appUser, setAppUser] = useState(); + const [appUser, setAppUser] = useState(); function login() { const host = window.location.host === 'localhost:5173' ? 'http://localhost:8080' : window.location.origin window.open(host + '/oauth2/authorization/github', '_self') } + function logout() { + const host = window.location.host === 'localhost:5173' ? 'http://localhost:8080' : window.location.origin + + window.open(host + '/logout', '_self') + } + useEffect(() => { axios.get("/api/auth/me") .then((r) => setAppUser(r.data)) @@ -24,6 +30,7 @@ function LoginPage() { appUser && ( <>

    Sie sind als {appUser?.login} angemeldet

    + ) } diff --git a/frontend/src/types/types.ts b/frontend/src/types/types.ts index 898bd4a..e6c09a9 100644 --- a/frontend/src/types/types.ts +++ b/frontend/src/types/types.ts @@ -64,4 +64,8 @@ export type AllInsurancesResponse = { export type AppUser = { id: string, login: string, +}; + +export type ProtectedRoutesProps = { + appUser: AppUser | null; }; \ No newline at end of file From 3cfe2b0d3482088cfcaf4d87b8e1a8182cfb049e Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Wed, 13 Dec 2023 03:16:09 +0100 Subject: [PATCH 05/14] Change SecurityConfig, ProtectedRoutes, types.ts, Header.tsx (logout function), App.tsx, remove LoginPage.tsx, move some files, change some styling --- .../AuthenticationController.java} | 5 +- .../{security => models}/AppUser.java | 2 +- .../securedController/SecuredController.java | 15 ------ .../security/SecurityConfig.java | 2 +- frontend/src/App.css | 9 ++++ frontend/src/App.tsx | 54 +++++++++++++------ frontend/src/ProtectedRoutes.tsx | 9 ++-- frontend/src/components/header/Header.css | 14 ++++- frontend/src/components/header/Header.tsx | 13 ++++- frontend/src/pages/AddPage.css | 15 ------ frontend/src/pages/AddPage.tsx | 3 +- frontend/src/pages/LoginPage.css | 8 --- frontend/src/pages/LoginPage.tsx | 41 -------------- frontend/src/pages/SharedComponents.css | 16 ++++++ frontend/src/types/types.ts | 2 +- 15 files changed, 98 insertions(+), 110 deletions(-) rename backend/src/main/java/com/github/iskrendev/insuranceprogram/{security/AuthController.java => controllers/AuthenticationController.java} (85%) rename backend/src/main/java/com/github/iskrendev/insuranceprogram/{security => models}/AppUser.java (81%) delete mode 100644 backend/src/main/java/com/github/iskrendev/insuranceprogram/securedController/SecuredController.java delete mode 100644 frontend/src/pages/AddPage.css delete mode 100644 frontend/src/pages/LoginPage.css delete mode 100644 frontend/src/pages/LoginPage.tsx diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/controllers/AuthenticationController.java similarity index 85% rename from backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java rename to backend/src/main/java/com/github/iskrendev/insuranceprogram/controllers/AuthenticationController.java index 5792148..efb26f6 100644 --- a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AuthController.java +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/controllers/AuthenticationController.java @@ -1,5 +1,6 @@ -package com.github.iskrendev.insuranceprogram.security; +package com.github.iskrendev.insuranceprogram.controllers; +import com.github.iskrendev.insuranceprogram.models.AppUser; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.web.bind.annotation.GetMapping; @@ -8,7 +9,7 @@ @RestController @RequestMapping("/api/auth") -public class AuthController { +public class AuthenticationController { @GetMapping("/me") public AppUser getMe() { diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AppUser.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/models/AppUser.java similarity index 81% rename from backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AppUser.java rename to backend/src/main/java/com/github/iskrendev/insuranceprogram/models/AppUser.java index ee141b4..80e8746 100644 --- a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/AppUser.java +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/models/AppUser.java @@ -1,4 +1,4 @@ -package com.github.iskrendev.insuranceprogram.security; +package com.github.iskrendev.insuranceprogram.models; import lombok.Builder; import org.springframework.data.annotation.Id; diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/securedController/SecuredController.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/securedController/SecuredController.java deleted file mode 100644 index 6c8cb69..0000000 --- a/backend/src/main/java/com/github/iskrendev/insuranceprogram/securedController/SecuredController.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.iskrendev.insuranceprogram.securedController; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/secured") -public class SecuredController { - @GetMapping - public String secured() { - return "Hello from secured endpoint"; - } - -} diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java index 7f77033..863dc39 100644 --- a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java @@ -23,7 +23,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(a -> a - .requestMatchers("/api/secured/**").authenticated() + .requestMatchers("/api/**").authenticated() .anyRequest().permitAll() ) .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)) diff --git a/frontend/src/App.css b/frontend/src/App.css index b9d355d..31e408b 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -40,3 +40,12 @@ .read-the-docs { color: #888; } + +.login-button { + width: 250px; + height: 40px; + position: absolute; + transform: translateX(-50%); + color: #f5f5f5; + box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 352d472..9698b78 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,34 +1,58 @@ import Header from "./components/header/Header.tsx"; import './App.css' -import {Link, Route, Routes} from "react-router-dom"; -import LoginPage from './pages/LoginPage.tsx'; +import {useNavigate, Route, Routes} from "react-router-dom"; import HomePage from './pages/HomePage.tsx'; import AddPage from './pages/AddPage.tsx'; import DetailsPage from "./pages/DetailsPage.tsx"; import EditPage from "./pages/EditPage.tsx"; import ProtectedRoutes from "./ProtectedRoutes.tsx"; import {AppUser} from "./types/types.ts"; +import {useEffect, useState} from "react"; +import axios from "axios"; function App() { + const [appUser, setAppUser] = useState(); + + function login() { + const host = window.location.host === 'localhost:5173' ? 'http://localhost:8080' : window.location.origin + window.open(host + '/oauth2/authorization/github', '_self') + } + + const navigate = useNavigate(); + + useEffect(() => { + axios.get("/api/auth/me") + .then((response) => { + setAppUser(response.data); + + if (response.data) { + navigate('/home'); + } + }) + .catch((e) => console.log(e)) + }, []); + + if (!appUser) { + return ( +
    + +
    + ); + } return ( <> -
    +
    - - Secured -
    - )}/> - } /> - }/> - } /> - } /> - } /> - } /> + }> + }/> + }/> + }/> + }/> + ) } -export default App \ No newline at end of file +export default App; \ No newline at end of file diff --git a/frontend/src/ProtectedRoutes.tsx b/frontend/src/ProtectedRoutes.tsx index 3a5d4b2..08bf5d9 100644 --- a/frontend/src/ProtectedRoutes.tsx +++ b/frontend/src/ProtectedRoutes.tsx @@ -1,11 +1,10 @@ import {ProtectedRoutesProps} from "./types/types.ts"; - - import {Navigate, Outlet} from "react-router-dom"; + function ProtectedRoutes(props: Readonly) { - const isAuth =props.appUser !== null + const isAuth = props.appUser !== null && props.appUser !== undefined; - return isAuth ? : + return isAuth ? : ; } -export default ProtectedRoutes \ No newline at end of file +export default ProtectedRoutes; \ No newline at end of file diff --git a/frontend/src/components/header/Header.css b/frontend/src/components/header/Header.css index 37bd94a..bce2d77 100644 --- a/frontend/src/components/header/Header.css +++ b/frontend/src/components/header/Header.css @@ -25,8 +25,18 @@ margin-right: 25px; } -.logout-icon { - margin-right: 5px; +.button-logout-icon { + background: none; + border: none; + padding: 0; + cursor: pointer; + outline: none; +} + +.button-logout-icon:hover, +.button-logout-icon:focus { + outline: none; + border: none; } .spacer { diff --git a/frontend/src/components/header/Header.tsx b/frontend/src/components/header/Header.tsx index 63ab723..6ffad6a 100644 --- a/frontend/src/components/header/Header.tsx +++ b/frontend/src/components/header/Header.tsx @@ -7,6 +7,10 @@ import StatisticsIcon from "../svg/StatisticsIcon.tsx"; function Header() { const location = useLocation(); const isLoginPage = location.pathname === '/login'; + const logout = () => { + const host = window.location.host === 'localhost:5173' ? 'http://localhost:8080' : window.location.origin + window.open(host + '/logout', '_self'); + }; if (!isLoginPage) { return ( @@ -18,12 +22,17 @@ function Header() { - +
); + } else { + return ( +
+
+ ); } } diff --git a/frontend/src/pages/AddPage.css b/frontend/src/pages/AddPage.css deleted file mode 100644 index fdb6a1b..0000000 --- a/frontend/src/pages/AddPage.css +++ /dev/null @@ -1,15 +0,0 @@ -.button-container { - margin-top: 10px; - text-align: center; -} - -.button-save { - width: 250px; - height: 40px; - position: absolute; - left: 50%; - bottom: -22%; - transform: translateX(-50%); - color: #f5f5f5; - box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); -} diff --git a/frontend/src/pages/AddPage.tsx b/frontend/src/pages/AddPage.tsx index 6a1aebf..5f851b6 100644 --- a/frontend/src/pages/AddPage.tsx +++ b/frontend/src/pages/AddPage.tsx @@ -1,4 +1,3 @@ -import "./AddPage.css"; import "./SharedComponents.css"; import React, {useState} from 'react'; import axios, {AxiosError} from "axios"; @@ -103,7 +102,7 @@ function AddPage() { handleOnChangeText={setCity}/> - diff --git a/frontend/src/pages/LoginPage.css b/frontend/src/pages/LoginPage.css deleted file mode 100644 index 8a13f92..0000000 --- a/frontend/src/pages/LoginPage.css +++ /dev/null @@ -1,8 +0,0 @@ -.button-login { - width: 250px; - height: 40px; - position: absolute; - transform: translateX(-50%); - color: #f5f5f5; - box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); -} \ No newline at end of file diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx deleted file mode 100644 index 354361b..0000000 --- a/frontend/src/pages/LoginPage.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import "./LoginPage.css"; -import {AppUser} from "../types/types.ts"; -import {useEffect, useState} from "react"; -import axios from "axios"; - -function LoginPage() { - const [appUser, setAppUser] = useState(); - function login() { - const host = window.location.host === 'localhost:5173' ? 'http://localhost:8080' : window.location.origin - - window.open(host + '/oauth2/authorization/github', '_self') - } - - function logout() { - const host = window.location.host === 'localhost:5173' ? 'http://localhost:8080' : window.location.origin - - window.open(host + '/logout', '_self') - } - - useEffect(() => { - axios.get("/api/auth/me") - .then((r) => setAppUser(r.data)) - .catch((e) => console.log(e)) - }, []); - - return ( - <> - { !appUser && } - { - appUser && ( - <> -

Sie sind als {appUser?.login} angemeldet

- - - ) - } - - ) -} - -export default LoginPage; \ No newline at end of file diff --git a/frontend/src/pages/SharedComponents.css b/frontend/src/pages/SharedComponents.css index c6d6a85..0064001 100644 --- a/frontend/src/pages/SharedComponents.css +++ b/frontend/src/pages/SharedComponents.css @@ -100,4 +100,20 @@ content: '\2212'; font-weight: bold; font-size: 24px; +} + +.button-container { + margin-top: 10px; + text-align: center; +} + +.button-save { + width: 250px; + height: 40px; + position: absolute; + left: 50%; + bottom: 10px; + transform: translateX(-50%); + color: #f5f5f5; + box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); } \ No newline at end of file diff --git a/frontend/src/types/types.ts b/frontend/src/types/types.ts index e6c09a9..87df668 100644 --- a/frontend/src/types/types.ts +++ b/frontend/src/types/types.ts @@ -67,5 +67,5 @@ export type AppUser = { }; export type ProtectedRoutesProps = { - appUser: AppUser | null; + appUser: AppUser | null | undefined; }; \ No newline at end of file From 62e8dc2ce70d76261844c55218584d4c08f01de5 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Wed, 13 Dec 2023 03:39:58 +0100 Subject: [PATCH 06/14] Change myapp.environment --- backend/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index d41ef75..d1de1f5 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -3,4 +3,4 @@ spring.mvc.hiddenmethod.filter.enabled= true spring.security.oauth2.client.registration.github.client-id=${GITHUB_ID} spring.security.oauth2.client.registration.github.client-secret=${GITHUB_SECRET} spring.security.oauth2.client.registration.github.scope=none -myapp.environment=${ENVIRONMENT} \ No newline at end of file +myapp.environment=development \ No newline at end of file From 8ae9205ecf2e690fa90ff1c544f7d7127094e156 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Wed, 13 Dec 2023 03:49:45 +0100 Subject: [PATCH 07/14] Change application.properties and SecurityConfig --- .../iskrendev/insuranceprogram/security/SecurityConfig.java | 2 +- backend/src/main/resources/application.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java index 863dc39..ae12db2 100644 --- a/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java +++ b/backend/src/main/java/com/github/iskrendev/insuranceprogram/security/SecurityConfig.java @@ -15,7 +15,7 @@ @EnableWebSecurity public class SecurityConfig { - @Value("${myapp.environment}") + @Value("local") private String environment; @Bean diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index d1de1f5..a9d7664 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -3,4 +3,4 @@ spring.mvc.hiddenmethod.filter.enabled= true spring.security.oauth2.client.registration.github.client-id=${GITHUB_ID} spring.security.oauth2.client.registration.github.client-secret=${GITHUB_SECRET} spring.security.oauth2.client.registration.github.scope=none -myapp.environment=development \ No newline at end of file +myapp.environment=local \ No newline at end of file From c2c1e53e2ec187c8a13f3fafff2aca6de6d357e3 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Wed, 13 Dec 2023 15:22:33 +0100 Subject: [PATCH 08/14] Fix CI error --- backend/src/test/resources/application.properties | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties index 8b5144d..e2c8364 100644 --- a/backend/src/test/resources/application.properties +++ b/backend/src/test/resources/application.properties @@ -1 +1,6 @@ -de.flapdoodle.mongodb.embedded.version=6.0.1 \ No newline at end of file +de.flapdoodle.mongodb.embedded.version=6.0.1 +spring.mvc.hiddenmethod.filter.enabled= true +spring.security.oauth2.client.registration.github.client-id=1 +spring.security.oauth2.client.registration.github.client-secret=1 +spring.security.oauth2.client.registration.github.scope=1 +myapp.environment=local \ No newline at end of file From 71d4aba574aaa67b4a32ddea7c0d56d3dcb851a8 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Wed, 13 Dec 2023 15:47:42 +0100 Subject: [PATCH 09/14] Change application.properties, add @WithMockUser to controllers --- .../controllers/AllInsurancesControllerTest.java | 3 +++ .../controllers/LifeInsuranceControllerTest.java | 10 ++++++++++ .../controllers/PropertyInsuranceControllerTest.java | 10 ++++++++++ .../controllers/VehicleInsuranceControllerTest.java | 10 ++++++++++ backend/src/test/resources/application.properties | 4 ++-- 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/AllInsurancesControllerTest.java b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/AllInsurancesControllerTest.java index 920bf79..ce558ec 100644 --- a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/AllInsurancesControllerTest.java +++ b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/AllInsurancesControllerTest.java @@ -13,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; @@ -44,6 +45,7 @@ class AllInsurancesControllerTest { @Test @DirtiesContext + @WithMockUser void getAllInsurances_whenNoInsurancesAreInLists_thenReturnEmptyLists() throws Exception { mockMvc.perform(get(BASE_URI)) .andExpect(jsonPath("$.lifeInsurances", hasSize(0))) @@ -53,6 +55,7 @@ void getAllInsurances_whenNoInsurancesAreInLists_thenReturnEmptyLists() throws E @Test @DirtiesContext + @WithMockUser void getAllInsurances_whenOneInsuranceIsInEachList_thenReturnLists() throws Exception { LifeInsurance lifeInsurance = LifeInsurance.builder() .id("1") diff --git a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/LifeInsuranceControllerTest.java b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/LifeInsuranceControllerTest.java index f683f28..b2a47b9 100644 --- a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/LifeInsuranceControllerTest.java +++ b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/LifeInsuranceControllerTest.java @@ -11,6 +11,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; @@ -34,6 +35,7 @@ class LifeInsuranceControllerTest { @Test @DirtiesContext + @WithMockUser void getAllLifeInsurances_whenNoLifeInsuranceIsInList_thenReturnEmptyList() throws Exception { mockMvc.perform(get(BASE_URI)) .andExpect(status().isOk()) @@ -42,6 +44,7 @@ void getAllLifeInsurances_whenNoLifeInsuranceIsInList_thenReturnEmptyList() thro @Test @DirtiesContext + @WithMockUser void getAllLifeInsurances_whenOneLifeInsuranceIsInList_thenReturnList() throws Exception { LifeInsurance lifeInsurance = LifeInsurance.builder() .id("1") @@ -72,6 +75,7 @@ void getAllLifeInsurances_whenOneLifeInsuranceIsInList_thenReturnList() throws E @Test @DirtiesContext + @WithMockUser void getLifeInsuranceById_whenIdIsValid_thenReturnInsurance() throws Exception { LifeInsurance lifeInsurance = LifeInsurance.builder() .id("1") @@ -100,6 +104,7 @@ void getLifeInsuranceById_whenIdIsValid_thenReturnInsurance() throws Exception { @Test @DirtiesContext + @WithMockUser void getLifeInsuranceById_whenIdIsNotValid_thenThrowException() throws Exception { mockMvc.perform(get(BASE_URI + "/invalidId")) .andExpect(status().isNotFound()) @@ -108,6 +113,7 @@ void getLifeInsuranceById_whenIdIsNotValid_thenThrowException() throws Exception @Test @DirtiesContext + @WithMockUser void addLifeInsurance_whenDataIsComplete_thenReturnCompleteInsurance() throws Exception { LifeInsuranceDTO newLifeInsurance = LifeInsuranceDTO.builder() .firstName("TestFirstName") @@ -136,6 +142,7 @@ void addLifeInsurance_whenDataIsComplete_thenReturnCompleteInsurance() throws Ex @Test @DirtiesContext + @WithMockUser void addLifeInsurance_whenJustOneFieldIsFilledOut_thenReturnNullForEmptyFields() throws Exception { LifeInsuranceDTO newLifeInsurance = LifeInsuranceDTO.builder() .firstName("TestFirstName") @@ -163,6 +170,7 @@ void addLifeInsurance_whenJustOneFieldIsFilledOut_thenReturnNullForEmptyFields() @Test @DirtiesContext + @WithMockUser void updateLifeInsurance_whenInsuranceIdExistsInDb_thenReturnUpdatedInsurance() throws Exception { LifeInsurance lifeInsuranceBefore = LifeInsurance.builder() .id("1") @@ -233,6 +241,7 @@ void updateLifeInsurance_whenInsuranceIdExistsInDb_thenReturnUpdatedInsurance() @Test @DirtiesContext + @WithMockUser void updateLifeInsurance_whenInsuranceIdDoesNotExistsInDb_thenThrowException() throws Exception { LifeInsuranceUpdateDTO lifeInsuranceUpdateDTO = LifeInsuranceUpdateDTO.builder() .firstName("TestFirstName") @@ -260,6 +269,7 @@ void updateLifeInsurance_whenInsuranceIdDoesNotExistsInDb_thenThrowException() t @Test @DirtiesContext + @WithMockUser void deleteLifeInsurance() throws Exception { LifeInsurance lifeInsurance = LifeInsurance.builder() .id("1") diff --git a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/PropertyInsuranceControllerTest.java b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/PropertyInsuranceControllerTest.java index a50a35e..d512113 100644 --- a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/PropertyInsuranceControllerTest.java +++ b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/PropertyInsuranceControllerTest.java @@ -9,6 +9,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; @@ -32,6 +33,7 @@ class PropertyInsuranceControllerTest { @Test @DirtiesContext + @WithMockUser void getAllPropertyInsurances_whenNoPropertyInsuranceIsInList_thenReturnEmptyList() throws Exception { mockMvc.perform(get(BASE_URI)) .andExpect(status().isOk()) @@ -40,6 +42,7 @@ void getAllPropertyInsurances_whenNoPropertyInsuranceIsInList_thenReturnEmptyLis @Test @DirtiesContext + @WithMockUser void getAllPropertyInsurances_whenOnePropertyInsuranceIsInList_thenReturnList() throws Exception { PropertyInsurance propertyInsurance = PropertyInsurance.builder() .id("1") @@ -71,6 +74,7 @@ void getAllPropertyInsurances_whenOnePropertyInsuranceIsInList_thenReturnList() @Test @DirtiesContext + @WithMockUser void getPropertyInsuranceById_whenIdIsValid_thenReturnInsurance() throws Exception { PropertyInsurance propertyInsurance = PropertyInsurance.builder() .id("1") @@ -100,6 +104,7 @@ void getPropertyInsuranceById_whenIdIsValid_thenReturnInsurance() throws Excepti @Test @DirtiesContext + @WithMockUser void getPropertyInsuranceById_whenIdIsNotValid_thenThrowException() throws Exception { mockMvc.perform(get(BASE_URI + "/invalidId")) .andExpect(status().isNotFound()) @@ -108,6 +113,7 @@ void getPropertyInsuranceById_whenIdIsNotValid_thenThrowException() throws Excep @Test @DirtiesContext + @WithMockUser void addPropertyInsurance_whenDataIsComplete_thenReturnCompleteInsurance() throws Exception { PropertyInsuranceDTO newPropertyInsurance = PropertyInsuranceDTO.builder() .firstName("TestFirstName") @@ -137,6 +143,7 @@ void addPropertyInsurance_whenDataIsComplete_thenReturnCompleteInsurance() throw @Test @DirtiesContext + @WithMockUser void addPropertyInsurance_whenJustOneFieldIsFilledOut_thenReturnNullForEmptyFields() throws Exception { PropertyInsuranceDTO newPropertyInsurance = PropertyInsuranceDTO.builder() .firstName("TestFirstName") @@ -166,6 +173,7 @@ void addPropertyInsurance_whenJustOneFieldIsFilledOut_thenReturnNullForEmptyFiel @Test @DirtiesContext + @WithMockUser void updatePropertyInsurance_whenInsuranceIdExistsInDb_thenReturnUpdatedInsurance() throws Exception { PropertyInsurance propertyInsuranceBefore = PropertyInsurance.builder() .id("1") @@ -239,6 +247,7 @@ void updatePropertyInsurance_whenInsuranceIdExistsInDb_thenReturnUpdatedInsuranc @Test @DirtiesContext + @WithMockUser void updatePropertyInsurance_whenInsuranceIdDoesNotExistsInDb_thenThrowException() throws Exception { PropertyInsuranceUpdateDTO propertyInsuranceUpdateDTO = PropertyInsuranceUpdateDTO.builder() .firstName("TestFirstName") @@ -267,6 +276,7 @@ void updatePropertyInsurance_whenInsuranceIdDoesNotExistsInDb_thenThrowException @Test @DirtiesContext + @WithMockUser void deletePropertyInsurance() throws Exception { PropertyInsurance propertyInsurance = PropertyInsurance.builder() .id("1") diff --git a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/VehicleInsuranceControllerTest.java b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/VehicleInsuranceControllerTest.java index 2c255bf..440b350 100644 --- a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/VehicleInsuranceControllerTest.java +++ b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/VehicleInsuranceControllerTest.java @@ -9,6 +9,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; @@ -32,6 +33,7 @@ class VehicleInsuranceControllerTest { @Test @DirtiesContext + @WithMockUser void getAllVehicleInsurances_whenNoVehicleInsuranceIsInList_thenReturnEmptyList() throws Exception { mockMvc.perform(get(BASE_URI)) .andExpect(status().isOk()) @@ -40,6 +42,7 @@ void getAllVehicleInsurances_whenNoVehicleInsuranceIsInList_thenReturnEmptyList( @Test @DirtiesContext + @WithMockUser void getAllVehicleInsurances_whenListContainsInsurances_thenReturnListOfInsurances() throws Exception { VehicleInsurance vehicleInsurance = VehicleInsurance.builder() .id("1") @@ -71,6 +74,7 @@ void getAllVehicleInsurances_whenListContainsInsurances_thenReturnListOfInsuranc @Test @DirtiesContext + @WithMockUser void getVehicleInsuranceById_whenIdIsValid_thenReturnInsurance() throws Exception { VehicleInsurance vehicleInsurance = VehicleInsurance.builder() .id("1") @@ -100,6 +104,7 @@ void getVehicleInsuranceById_whenIdIsValid_thenReturnInsurance() throws Exceptio @Test @DirtiesContext + @WithMockUser void getVehicleInsuranceById_whenIdIsNotValid_thenThrowException() throws Exception { mockMvc.perform(get(BASE_URI + "/invalidId")) .andExpect(status().isNotFound()) @@ -108,6 +113,7 @@ void getVehicleInsuranceById_whenIdIsNotValid_thenThrowException() throws Except @Test @DirtiesContext + @WithMockUser void addVehicleInsurance_whenDataIsComplete_thenReturnCompleteInsurance() throws Exception { VehicleInsuranceDTO newVehicleInsurance = VehicleInsuranceDTO.builder() .firstName("TestFirstName") @@ -137,6 +143,7 @@ void addVehicleInsurance_whenDataIsComplete_thenReturnCompleteInsurance() throws @Test @DirtiesContext + @WithMockUser void addVehicleInsurance_whenJustOneFieldIsFilledOut_thenReturnNullForEmptyFields() throws Exception { VehicleInsuranceDTO newVehicleInsurance = VehicleInsuranceDTO.builder() .firstName("TestFirstName") @@ -167,6 +174,7 @@ void addVehicleInsurance_whenJustOneFieldIsFilledOut_thenReturnNullForEmptyField @Test @DirtiesContext + @WithMockUser void updateVehicleInsurance_whenInsuranceIdExistsInDb_thenReturnUpdatedInsurance() throws Exception { VehicleInsurance vehicleInsuranceBefore = VehicleInsurance.builder() .id("1") @@ -243,6 +251,7 @@ void updateVehicleInsurance_whenInsuranceIdExistsInDb_thenReturnUpdatedInsurance @Test @DirtiesContext + @WithMockUser void updateVehicleInsurance_whenInsuranceIdDoesNotExistsInDb_thenThrowException() throws Exception { VehicleInsuranceUpdateDTO vehicleInsuranceUpdateDTO = VehicleInsuranceUpdateDTO.builder() .firstName("TestFirstName") @@ -272,6 +281,7 @@ void updateVehicleInsurance_whenInsuranceIdDoesNotExistsInDb_thenThrowException( @Test @DirtiesContext + @WithMockUser void deleteVehicleInsurance() throws Exception { VehicleInsurance vehicleInsurance = VehicleInsurance.builder() .id("1") diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties index e2c8364..0c5aff2 100644 --- a/backend/src/test/resources/application.properties +++ b/backend/src/test/resources/application.properties @@ -1,6 +1,6 @@ de.flapdoodle.mongodb.embedded.version=6.0.1 spring.mvc.hiddenmethod.filter.enabled= true -spring.security.oauth2.client.registration.github.client-id=1 -spring.security.oauth2.client.registration.github.client-secret=1 +spring.security.oauth2.client.registration.github.client-id="1" +spring.security.oauth2.client.registration.github.client-secret="1" spring.security.oauth2.client.registration.github.scope=1 myapp.environment=local \ No newline at end of file From a2340c38c9e179cd0e2daf2f743af5db4cc49138 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Wed, 13 Dec 2023 15:48:40 +0100 Subject: [PATCH 10/14] Add pom.xml dependency --- backend/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/pom.xml b/backend/pom.xml index afcbe81..d233957 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -29,6 +29,11 @@ 4.9.3 test
+ + org.springframework.security + spring-security-test + test + org.springframework.boot spring-boot-starter-web From 03a78849bc20262da235260be1988e2989534c00 Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Wed, 13 Dec 2023 19:11:35 +0100 Subject: [PATCH 11/14] Edit DeleteConfirmationModal, remove code smell --- frontend/src/modals/DeleteConfirmationModal.css | 9 ++------- frontend/src/modals/DeleteConfirmationModal.tsx | 8 +++----- frontend/src/pages/HomePage.tsx | 12 +++++------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/frontend/src/modals/DeleteConfirmationModal.css b/frontend/src/modals/DeleteConfirmationModal.css index b42ce43..55d0a0b 100644 --- a/frontend/src/modals/DeleteConfirmationModal.css +++ b/frontend/src/modals/DeleteConfirmationModal.css @@ -24,18 +24,13 @@ display: flex; align-items: center; justify-content: center; -} - -.centered-content { - display: flex; - align-items: center; - margin-left: -10px; + margin-right: 16px; } .modal-title { font-size: 1.7em; font-weight: bold; - margin-bottom: 10px; + margin-bottom: 3px; text-align: center; } diff --git a/frontend/src/modals/DeleteConfirmationModal.tsx b/frontend/src/modals/DeleteConfirmationModal.tsx index f954635..c5bf833 100644 --- a/frontend/src/modals/DeleteConfirmationModal.tsx +++ b/frontend/src/modals/DeleteConfirmationModal.tsx @@ -9,7 +9,7 @@ interface DeleteConfirmationModalProps { } const DeleteConfirmationModal = (props: DeleteConfirmationModalProps) => { - const { show, handleClose, handleConfirm } = props; + const {show, handleClose, handleConfirm} = props; const handleCloseKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter' || event.key === ' ') { @@ -28,10 +28,8 @@ const DeleteConfirmationModal = (props: DeleteConfirmationModalProps) => {
-
- -
Bestätigung
-
+ +
Bestätigung

Sind Sie sicher, dass Sie diesen Versicherungseintrag löschen möchten?

diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index f3db65c..9841828 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -25,19 +25,17 @@ function HomePage() { }, []); return ( - <>

Übersicht

-
- - - -
+
+ + +
- +
); } From c8b26d5c1295b672055da2fd851669b4c10fc58c Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Thu, 14 Dec 2023 03:10:14 +0100 Subject: [PATCH 12/14] Change login button position, change input fields size, add website name --- frontend/index.html | 2 +- frontend/src/App.css | 4 +++- frontend/src/pages/SharedComponents.css | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index e4b78ea..f31ccf9 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - Vite + React + TS + Insurance Manager Plus
diff --git a/frontend/src/App.css b/frontend/src/App.css index 31e408b..1f963f4 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -45,7 +45,9 @@ width: 250px; height: 40px; position: absolute; - transform: translateX(-50%); + top: 50%; + left: 50%; + transform: translate(-50%, -50%); color: #f5f5f5; box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); } diff --git a/frontend/src/pages/SharedComponents.css b/frontend/src/pages/SharedComponents.css index 0064001..5b7cb32 100644 --- a/frontend/src/pages/SharedComponents.css +++ b/frontend/src/pages/SharedComponents.css @@ -35,6 +35,10 @@ margin-bottom: -10px; } +.form-section input { + padding-right: 140px; +} + .form-section input[name="type"] { visibility: hidden; } From 68d0abfa64ef18cea5ef14573d54d89bc3e3731d Mon Sep 17 00:00:00 2001 From: iskrenradev Date: Thu, 14 Dec 2023 05:37:04 +0100 Subject: [PATCH 13/14] Change InsuranceList.tsx, change save button position --- .../src/components/content/InsuranceList.tsx | 33 ++++++++++++------- frontend/src/pages/SharedComponents.css | 2 +- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/content/InsuranceList.tsx b/frontend/src/components/content/InsuranceList.tsx index 3fe6830..e1ba10f 100644 --- a/frontend/src/components/content/InsuranceList.tsx +++ b/frontend/src/components/content/InsuranceList.tsx @@ -7,33 +7,42 @@ function InsuranceList(props: Readonly) { const [sortedInsurances, setSortedInsurances] = useState([...props.insurances]); const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc"); const [isAccordionOpen, setIsAccordionOpen] = useState(false); + const [isListRendered, setIsListRendered] = useState(false); const handleAccordionToggle = () => { setIsAccordionOpen(!isAccordionOpen); + setIsListRendered(true); }; const handleSortToggle = () => { - const newSortOrder = sortOrder === "asc" ? "desc" : "asc"; - setSortOrder(newSortOrder); + if (isListRendered) { + const newSortOrder = sortOrder === "asc" ? "desc" : "asc"; + setSortOrder(newSortOrder); - const sorted = [...props.insurances].sort((a, b) => { - const nameA = `${a.firstName} ${a.familyName}`.toUpperCase(); - const nameB = `${b.firstName} ${b.familyName}`.toUpperCase(); + const sorted = [...props.insurances].sort((a, b) => { + const nameA = `${a.firstName} ${a.familyName}`.toUpperCase(); + const nameB = `${b.firstName} ${b.familyName}`.toUpperCase(); - return newSortOrder === "asc" ? nameA.localeCompare(nameB) : nameB.localeCompare(nameA); - }); + return newSortOrder === "asc" ? nameA.localeCompare(nameB) : nameB.localeCompare(nameA); + }); - setSortedInsurances(sorted); + setSortedInsurances(sorted); + } }; return (

{props.headerText}

- - + + {isListRendered && ( + + )}
{isAccordionOpen && (
    diff --git a/frontend/src/pages/SharedComponents.css b/frontend/src/pages/SharedComponents.css index 5b7cb32..223099f 100644 --- a/frontend/src/pages/SharedComponents.css +++ b/frontend/src/pages/SharedComponents.css @@ -116,7 +116,7 @@ height: 40px; position: absolute; left: 50%; - bottom: 10px; + bottom: 5%; transform: translateX(-50%); color: #f5f5f5; box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.1); From 89edaa368a202d929516b3be1ce608e5947ab5fe Mon Sep 17 00:00:00 2001 From: Iskren Radev Date: Mon, 1 Jan 2024 18:50:22 +0100 Subject: [PATCH 14/14] Add integration tests --- .../AuthenticationControllerTest.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/AuthenticationControllerTest.java diff --git a/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/AuthenticationControllerTest.java b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/AuthenticationControllerTest.java new file mode 100644 index 0000000..e78ab9a --- /dev/null +++ b/backend/src/test/java/com/github/iskrendev/insuranceprogram/controllers/AuthenticationControllerTest.java @@ -0,0 +1,50 @@ +package com.github.iskrendev.insuranceprogram.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.iskrendev.insuranceprogram.models.AppUser; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oidcLogin; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class AuthenticationControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Test + @DirtiesContext + void getMe_whenLoggedIn_expectStatus200() throws Exception { + AppUser expectedAppUser = new AppUser(1, "test"); + String expectedJson = objectMapper.writeValueAsString(expectedAppUser); + mockMvc.perform(get("/api/auth/me") + .with(oidcLogin().userInfoToken(token -> { + token.claims(claim -> { + claim.put("id", "1"); + claim.put("login", "test"); + }); + })) + ) + .andExpect(status().isOk()) + .andExpect(content().json(expectedJson)); + } + + @Test + @DirtiesContext + void getMe_whenNotLoggedIn_expectStatus401() throws Exception { + mockMvc.perform(get("/api/auth/me")) + .andExpect(status().isUnauthorized()); + } +} \ No newline at end of file