diff --git a/jdk_21_maven/cs/rest/flight-search-api/.gitattributes b/jdk_21_maven/cs/rest/flight-search-api/.gitattributes
new file mode 100644
index 000000000..3b41682ac
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/.gitattributes
@@ -0,0 +1,2 @@
+/mvnw text eol=lf
+*.cmd text eol=crlf
diff --git a/jdk_21_maven/cs/rest/flight-search-api/.gitignore b/jdk_21_maven/cs/rest/flight-search-api/.gitignore
new file mode 100644
index 000000000..d5af496f1
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/.gitignore
@@ -0,0 +1,36 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Environment file
+.env
diff --git a/jdk_21_maven/cs/rest/flight-search-api/CODEOWNERS b/jdk_21_maven/cs/rest/flight-search-api/CODEOWNERS
new file mode 100644
index 000000000..de537767b
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/CODEOWNERS
@@ -0,0 +1,2 @@
+# These owners will be the default owners for everything in the repo.
+* @Rapter1990 @furkantarikgocmen
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/CODE_OF_CONDUCT.md b/jdk_21_maven/cs/rest/flight-search-api/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..d43f9f233
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/CONTRIBUTING.md b/jdk_21_maven/cs/rest/flight-search-api/CONTRIBUTING.md
new file mode 100644
index 000000000..c1807d793
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/CONTRIBUTING.md
@@ -0,0 +1,182 @@
+# Contributing Guidelines
+
+Thank you for considering contributing to the "afet-yonetim-sistemi" project! We appreciate your interest and welcome
+any contributions that can help us improve the project.
+
+Please take a moment to review these guidelines before making any contributions. Following these guidelines will help
+you maintain a collaborative and inclusive environment for everyone involved.
+
+## Table of Contents
+
+- [How to Contribute](#how-to-contribute)
+- [Reporting Issues](#reporting-issues)
+- [Submitting Changes](#submitting-changes)
+- [Code Style](#code-style)
+- [Commit Messages](#commit-messages)
+- [Testing](#testing)
+- [Documentation](#documentation)
+- [Community Guidelines](#community-guidelines)
+
+## How to Contribute
+
+Thank you for considering contributing to our project! To make the process smooth, please follow these steps:
+
+1. **Fork the Repository:**
+
+ - Visit our [GitHub repository](https://github.com/EmreKiziltoprak/CRM_experiment) and fork it to your GitHub account.
+
+2. **Create a New Branch:**
+
+ - Create a branch for your contribution by referring to
+ the [Naming Conventions of Branches](#branch-naming-conventions) for guidance.
+ - Ensure your feature branch is associated with the corresponding GitHub issue from the 'Development' section in
+ each issue page.
+
+3. **Make Changes:**
+
+ - Implement your changes in the branch you created.
+ - Thoroughly test your changes to ensure they work as expected.
+
+4. **Commit Changes:**
+
+ - Commit your changes with clear and concise messages. Refer to the [Commit Messages](#commit-messages) section for
+ guidance.
+
+5. **Push Changes:**
+
+ - Push your changes to your forked repository.
+
+6. **Open a Pull Request (PR):**
+
+ - Open a PR against the `main` branch of the original repository by referring to
+ the [Pull Request Naming Conventions](#pull-request-naming-conventions) for guidance.
+ - Provide a detailed description of your changes in the PR.
+ - Link relevant issues or discussions in the PR description.
+ - Follow the pull request naming conventions outlined below.
+ - PR title should contain the issue/feature/bug number.
+
+7. **Review and Feedback:**
+ - Once your PR is submitted, project maintainers will review it and provide feedback.
+ - Be responsive to feedback and make necessary changes.
+
+## Development Standards
+
+Our project adheres to the [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) for
+collaboration.
+Issues are managed on GitHub Discussions, with plans to transition them to Github.
+
+### Branch Naming Conventions:
+
+- `bugfix/{github-issue-number}/{optional-description}`
+- `development/{github-issue-number}/{optional-description}`
+- `refactor/{github-issue-number}/{optional-description}`
+- `hotfix/{github-issue-number}/{optional-description}`
+
+> Example : `development/1/add-feature-to-handle-user-authentication`
+
+### Pull Request Naming Conventions:
+
+- `#{github-issue-number} | {header-for-summary-of-development}`
+
+> Example : `#PR 1 | Add feature to handle user authentication`
+
+Here are some additional standards to keep in mind:
+
+- Link your feature branch to the corresponding GitHub issue.
+- `main` branch is protected.
+- PR should be linked to the relevant issue within the GitHub Project.
+- Only one code owner's approval is needed for merging feature branches.
+- PR should be squash-merged to avoid merge commit history.
+- PR should pass build and necessary tests before merging.
+- Releases should be tagged with a version.
+- Commit messages should be subjectless.
+- Resolve comments within the PR by the commenter.
+- PR should be concise and address one thing.
+- Avoid including secret/credential information.
+- Code should comply with existing coding standards; no new standards should be introduced without justification.
+
+## Reporting Issues
+
+If you encounter any bugs, have feature requests, or want to provide general feedback, please feel free to open a
+discussion
+in the "CRM_experiment" repository. When creating a discussion, please provide as much detail as possible,
+including:
+
+- A clear and descriptive title
+- Steps to reproduce the issue
+- Expected behavior
+- Actual behavior
+- Any error messages or logs
+
+## Submitting Changes
+
+When submitting changes, please ensure the following:
+
+- Your code adheres to the project's [code style guidelines](#code-style).
+- You have added appropriate tests for your changes (see [Testing](#testing)).
+- The documentation has been updated, if necessary (see [Documentation](#documentation)).
+- Your commits are clean and have clear commit messages (see [Commit Messages](#commit-messages)).
+
+## Code Style
+
+To maintain a consistent codebase, please follow the existing code style used in the project. If necessary, refer to any
+code style guidelines mentioned in the repository.
+
+## Commit Messages
+
+Writing clear and descriptive commit messages is essential for the maintainability of the project. Follow these
+guidelines when writing commit messages:
+
+- Limit the subject line to 50 characters.
+- Begin the subject line with a verb in the present tense (e.g., "Fix," "Add," "Update").
+- Use the imperative mood (e.g., "Fix bug" instead of "Fixed bug").
+- Provide additional details in the commit message body if necessary. Explain why the change was made and any relevant
+ information for reviewers.
+
+Example:
+
+```
+Add feature to handle user authentication
+
+- Implemented a new module for user authentication
+- Updated existing login page UI
+- Added new API endpoint for user registration
+```
+
+## Testing
+
+If you are contributing code changes, it is important to include appropriate tests to ensure the stability and
+functionality of the project. Follow these guidelines when writing tests:
+
+- Write tests that cover the new code you have added or modified.
+- Ensure your tests are easy to understand and maintain.
+- Run the existing test suite before submitting your changes to make sure they haven't introduced any regressions.
+- Provide instructions on how to run the tests if they require additional setup or specific commands.
+
+## Documentation
+
+Clear and up-to-date documentation is crucial for the project's users and contributors. If your contribution introduces
+new features, modifies existing functionality, or requires changes to the documentation, please make the necessary
+updates.
+
+Follow these guidelines for documenting your changes:
+
+- Update the relevant documentation files to reflect your changes.
+- Write clear and concise explanations.
+- Use code examples or screenshots, if appropriate, to enhance understanding.
+- Proofread your documentation to ensure it is accurate and free of errors.
+
+## Community Guidelines
+
+In addition to the technical aspects, it is important to follow the community guidelines to maintain a positive and
+inclusive environment for all contributors.
+
+- Be respectful and considerate of others' opinions and ideas.
+- Be open to feedback and willing to collaborate with other contributors.
+- Avoid personal attacks, offensive language, and harassment.
+- If you notice any inappropriate behavior, report it to the project maintainers.
+
+By adhering to these guidelines, we can work together to build a welcoming and productive community.
+
+Thank you for your interest in contributing to the "CRM_experiment" project! We appreciate your support and look
+forward to your contributions.
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/Dockerfile b/jdk_21_maven/cs/rest/flight-search-api/Dockerfile
new file mode 100644
index 000000000..3628f4f70
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/Dockerfile
@@ -0,0 +1,27 @@
+# Stage 1: Build stage
+FROM maven:3.9.7-amazoncorretto-21 AS build
+
+# Copy Maven files for dependency resolution
+COPY pom.xml ./
+COPY .mvn .mvn
+
+# Copy application source code
+COPY src src
+
+# Build the project and create the executable JAR
+RUN mvn clean install -DskipTests
+
+# Stage 2: Run stage
+FROM amazoncorretto:21
+
+# Set working directory
+WORKDIR flightsearchapi
+
+# Copy the JAR file from the build stage
+COPY --from=build target/*.jar flightsearchapi.jar
+
+# Expose port 1133
+EXPOSE 1133
+
+# Set the entrypoint command for running the application
+ENTRYPOINT ["java", "-jar", "flightsearchapi.jar"]
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/Jenkinsfile b/jdk_21_maven/cs/rest/flight-search-api/Jenkinsfile
new file mode 100644
index 000000000..ad672b403
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/Jenkinsfile
@@ -0,0 +1,71 @@
+pipeline {
+ agent any
+
+ environment {
+ GIT_REPO_URL = 'https://github.com/Rapter1990/flightsearchapi.git'
+ BRANCH_NAME = 'main'
+ DOCKERHUB_USERNAME = 'noyandocker'
+ DOCKER_IMAGE_NAME = 'flightsearchapi-jenkins'
+ }
+
+ stages {
+ stage('Checkout') {
+ steps {
+ script {
+ checkout([
+ $class: 'GitSCM',
+ branches: [[name: "*/${env.BRANCH_NAME}"]],
+ userRemoteConfigs: [[url: "${env.GIT_REPO_URL}"]]
+ ])
+ }
+ }
+ }
+
+ stage('Build') {
+ agent {
+ docker {
+ image 'maven:3.9.9-amazoncorretto-21-alpine'
+ }
+ }
+ steps {
+ sh 'mvn clean install'
+ }
+ }
+
+ stage('Build Docker Image') {
+ agent {
+ docker {
+ image 'docker:27.5.1'
+ }
+ }
+ steps {
+ sh "docker build -t ${env.DOCKERHUB_USERNAME}/${env.DOCKER_IMAGE_NAME}:latest ."
+ }
+ }
+
+ stage('Push Docker Image') {
+ agent {
+ docker {
+ image 'docker:27.5.1'
+ }
+ }
+ steps {
+ withDockerRegistry([credentialsId: 'docker-hub-credentials', url: '']) {
+ sh "docker push ${env.DOCKERHUB_USERNAME}/${env.DOCKER_IMAGE_NAME}:latest"
+ }
+ }
+ }
+
+ }
+
+ post {
+ always {
+ cleanWs(cleanWhenNotBuilt: false,
+ deleteDirs: true,
+ disableDeferredWipeout: true,
+ notFailBuild: true,
+ patterns: [[pattern: '.gitignore', type: 'INCLUDE'],
+ [pattern: '.propsfile', type: 'EXCLUDE']])
+ }
+ }
+}
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/LICENSE.md b/jdk_21_maven/cs/rest/flight-search-api/LICENSE.md
new file mode 100644
index 000000000..b208ddd05
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/LICENSE.md
@@ -0,0 +1,30 @@
+MIT License
+===========
+
+This project is licensed under the terms of the MIT license.
+
+MIT License
+-----------
+
+SPDX short identifier: MIT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the following conditions:
+
+1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+ Software.
+
+2. The Software is provided "as is," without warranty of any kind, express or implied, including but not limited to the
+ warranties of merchantability, fitness for a particular purpose, and non-infringement. In no event shall the authors
+ or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or
+ otherwise, arising from, out of, or in connection with the Software or the use or other dealings in the Software.
+
+3. Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or
+ otherwise to promote the sale, use or other dealings in this Software without prior written authorization.
+
+4. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software
+ developed by the project team (https://github.com/Rapter1990/flightsearchapi)."
+
+5. Redistributions of any form whatsoever must also include a copy of this license document in the distribution.
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/PULL_REQUEST_TEMPLATE.md b/jdk_21_maven/cs/rest/flight-search-api/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..b33f7cb69
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,49 @@
+### Description
+
+Please provide a brief description of the changes made in this pull request and the issue(s) it addresses.
+
+....Write Description....
+
+#### Related Issue
+
+Link to the issue this pull request addresses: #[issue number]
+
+### Checklist
+
+Before submitting your pull request, ensure the following:
+
+- [ ] **Title and Branch Naming Conventions:**
+
+ - The pull request title follows the
+ standard: [Pull Request Naming Conventions](CONTRIBUTING.md#pull-request-naming-conventions).
+ - The branch name follows one of the conventions outlined in
+ the [Branch Naming Conventions](CONTRIBUTING.md#branch-naming-conventions).
+
+- [ ] **Local Testing:**
+
+ - I have tested my changes locally on Postman, and they are working as expected.
+
+- [ ] **Code Quality:**
+
+ - The code is formatted according to the project's coding guidelines and style.
+ - The code has been reviewed to ensure its quality.
+ - The code does not contain any issues flagged by SonarLint.
+
+- [ ] **Documentation:**
+
+ - Necessary documentation has been added or existing documentation has been updated, specifically detailing changes
+ made in Postman.
+
+- [ ] **Testing:**
+
+ - Relevant unit tests have been written and included.
+ - Relevant integration tests have been written and included.
+
+- [ ] **Reviewers and Assignees:**
+
+ - Default reviewers have been assigned to this pull request.
+ - Assignees have been added if necessary.
+
+- [ ] **Labels and Associations:**
+ - The pull request is associated with its related issue.
+ - Appropriate labels have been applied.
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/README.md b/jdk_21_maven/cs/rest/flight-search-api/README.md
new file mode 100644
index 000000000..db8f71371
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/README.md
@@ -0,0 +1,476 @@
+# Case Study - Flight Search Api
+
+
+
+
+
+### 📖 Information
+
+
+
+ This project provides a Spring Boot API for managing user authentication,
+ airport management, flight management, and flight search functionalities. It
+ includes advanced logging and security features.
+
+
+ User Authentication:
+
+
+ User Registration: Register users with an email and password.
+
+
+ User Login: Authenticate users with email and password, generating
+ access and refresh tokens.
+
+ Create Airports: Accessible only to ADMIN users.
+
+
+ Retrieve Airports: Paginated airport list retrieval, accessible to both
+ ADMIN and USER roles.
+
+
+ Retrieve Specific Airport: Search by airport ID, accessible to both
+ ADMIN and USER roles.
+
+
+ Update Airports: Modify an existing airport by ID, accessible only to
+ ADMIN users.
+
+
+ Delete Airports: Remove airports by ID, accessible only to ADMIN
+ users.
+
+
+
+
+ Flight Management:
+
+
+ Create Flights: Accessible only to ADMIN users.
+
+
+ Retrieve Flights: Paginated flight list retrieval, accessible to both
+ ADMIN and USER roles.
+
+
+ Retrieve Specific Flight: Search by flight ID, accessible to both
+ ADMIN and USER roles.
+
+
+ Update Flights: Modify an existing flight by ID, accessible only to
+ ADMIN users.
+
+
+ Delete Flights: Remove flights by ID, accessible only to ADMIN
+ users.
+
+
+
+
+ Flight Search:
+
+
+ Search Flights: Search for flights based on departure, arrival airports,
+ and departure date. Accessible to both ADMIN and USER
+ roles. (One Way - Round-Trip)
+
+
+
+
+ Security:
+
+
+ User Roles: ADMIN and USER roles implemented using Spring Security.
+
+
JWT Authentication: Secure endpoints with JSON Web Tokens.
+
+
+
+ Logging:
+
+
+ Custom Logging Aspect: Logs details of REST controller method calls and
+ exceptions, including HTTP request and response details.
+
+
+
+
+### Technologies
+
+---
+- Java 21
+- Spring Boot 3.0
+- Restful API
+- Mapstruct
+- Open Api (Swagger)
+- Maven
+- Junit5
+- Mockito
+- Integration Tests
+- Docker
+- Docker Compose
+- CI/CD (Github Actions - Jenkins)
+- Postman
+- TestContainer
+- Mongo
+- Prometheus
+- Grafana
+- Kubernetes
+- JaCoCo (Test Report)
+
+### Postman
+
+```
+Import postman collection under postman_collection folder
+```
+
+
+### Prerequisites
+
+#### Define Variable in .env file
+
+```
+MONGO_DB_NAME=flightdatabase
+MONGO_DB_HOST=localhost
+MONGO_DB_PORT=27017
+```
+
+### Open Api (Swagger)
+
+```
+http://localhost:1133/swagger-ui/index.html
+```
+
+---
+
+### JaCoCo (Test Report)
+
+After the command named `mvn clean install` completes, the JaCoCo report will be available at:
+```
+target/site/jacoco/index.html
+```
+Navigate to the `target/site/jacoco/` directory.
+
+Open the `index.html` file in your browser to view the detailed coverage report.
+
+---
+
+### Maven, Docker and Kubernetes Running Process
+
+
+### Maven Run
+To build and run the application with `Maven`, please follow the directions shown below;
+
+```sh
+$ cd flightsearchapi
+$ mvn clean install
+$ mvn spring-boot:run
+```
+
+---
+
+### Docker Run
+The application can be built and run by the `Docker` engine. The `Dockerfile` has multistage build, so you do not need to build and run separately.
+
+Please follow directions shown below in order to build and run the application with Docker Compose file;
+
+```sh
+$ cd flightsearchapi
+$ docker-compose up -d
+```
+
+If you change anything in the project and run it on Docker, you can also use this command shown below
+
+```sh
+$ cd flightsearchapi
+$ docker-compose up --build
+```
+
+To monitor the application, you can use the following tools:
+
+- **Prometheus**:
+ Open in your browser at [http://localhost:9090](http://localhost:9090)
+ Prometheus collects and stores application metrics.
+
+- **Grafana**:
+ Open in your browser at [http://localhost:3000](http://localhost:3000)
+ Grafana provides a dashboard for visualizing the metrics.
+ **Default credentials**:
+ - Username: `admin`
+ - Password: `admin`
+
+- Define prometheus data source url, use this link shown below
+
+```
+http://prometheus:9090
+```
+
+---
+
+
+### Kubernetes Run
+To build and run the application with `Maven`, please follow the directions shown below;
+
+- Start Minikube
+
+```sh
+$ minikube start
+```
+
+- Open Minikube Dashboard
+
+```sh
+$ minikube dashboard
+```
+
+- To deploy the application on Kubernetes, apply the Kubernetes configuration file underneath k8s folder
+
+```sh
+$ kubectl apply -f k8s
+```
+
+- To open Prometheus, click tunnel url link provided by the command shown below to reach out Prometheus
+
+```sh
+minikube service prometheus-service
+```
+
+
+- To open Grafana, click tunnel url link provided by the command shown below to reach out Prometheus
+
+```sh
+minikube service grafana-service
+```
+
+- Define prometheus data source url, use this link shown below
+
+```
+http://prometheus-service.default.svc.cluster.local:9090
+```
+
+
+---
+### Docker Image Location
+
+```
+https://hub.docker.com/repository/docker/noyandocker/flightsearchapi/general
+https://hub.docker.com/repository/docker/noyandocker/flightsearchapi-jenkins/general
+```
+
+### Jenkins
+
+- Go to `jenkins` folder
+- Run `docker-compose up -d`
+- Open Jenkins in the browser via `localhost:8080`
+- Go to pipeline named `flightsearchapi`
+- Run Pipeline
+- Show `Pipeline Step` to verify if it succeeded or failed
+
+
+### Screenshots
+
+
+Click here to show the screenshots of project
+
Figure 1
+
+
Figure 2
+
+
Figure 3
+
+
Figure 4
+
+
Figure 5
+
+
Figure 6
+
+
Figure 7
+
+
Figure 8
+
+
Figure 9
+
+
Figure 10
+
+
Figure 11
+
+
Figure 12
+
+
Figure 13
+
+
Figure 14
+
+
Figure 15
+
+
Figure 16
+
+
Figure 17
+
+
Figure 18
+
+
Figure 19
+
+
Figure 20
+
+
Figure 21
+
+
Figure 22
+
+
Figure 23
+
+
Figure 24
+
+
+
+
+### Contributors
+
+- [Sercan Noyan Germiyanoğlu](https://github.com/Rapter1990)
+- [Furkan Tarık Göçmen](https://github.com/furkantarikgocmen)
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/SECURITY.md b/jdk_21_maven/cs/rest/flight-search-api/SECURITY.md
new file mode 100644
index 000000000..e0b7b5729
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/SECURITY.md
@@ -0,0 +1,49 @@
+# Security Policy
+
+At FlightSearchApi , we take the security of our software and community seriously. We appreciate your efforts
+to responsibly disclose any security vulnerabilities you may find.
+
+## Reporting a Vulnerability
+
+If you believe you have discovered a security vulnerability or have concerns regarding the security of our project,
+please help us by disclosing it to us in a responsible manner. We encourage you to follow the guidelines outlined below.
+
+To report a vulnerability:
+
+1. Create a new issue in this repository to report the vulnerability. Please provide as much information as possible to
+ help us understand and reproduce the issue.
+2. If you believe the vulnerability may have sensitive implications, please encrypt your findings using our PGP key (
+ available in the repository).
+3. We will review your report and respond to it as quickly as possible.
+4. We appreciate your patience and cooperation during the disclosure process.
+
+## Bug Bounty Program
+
+At this time, FlightSearchApi does not offer a formal bug bounty program for the project.
+
+## Supported Versions
+
+Please note that only the latest release of this project is officially supported with security updates. It is highly
+recommended to keep your software up-to-date with the latest version.
+
+## Security Considerations
+
+Here are some security considerations when using this project:
+
+1. Always ensure that you are using the latest version of the project to benefit from security updates and bug fixes.
+2. Regularly update your dependencies to include any security patches that may have been released.
+3. Follow best practices for secure coding and deployment, such as using strong and unique passwords, enabling
+ two-factor authentication, and limiting access privileges.
+4. Be cautious of the data you input or share, especially when interacting with external systems or APIs.
+
+## Contact
+
+If you need to contact us regarding any security-related matters or have any questions about the security of this
+project, you can reach out to us at:
+
+- Email: [javadevelopercasestudy@hotmail.com](mailto:javadevelopercasestudy@hotmail.com)
+
+We kindly request that you refrain from spam, social engineering, or any other malicious activities. We appreciate your
+cooperation in keeping our community safe and secure.
+
+Thank you for your contributions and for helping us maintain a secure ecosystem.
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/data/prometheus/config/prometheus.yml b/jdk_21_maven/cs/rest/flight-search-api/data/prometheus/config/prometheus.yml
new file mode 100644
index 000000000..c36980ffc
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/data/prometheus/config/prometheus.yml
@@ -0,0 +1,23 @@
+global:
+ scrape_interval: 120s # By default, scrape targets every 15 seconds.
+ evaluation_interval: 120s # By default, scrape targets every 15 seconds.
+
+# A scrape configuration containing exactly one endpoint to scrape:
+# Here it's Prometheus itself.
+scrape_configs:
+ # The job name is added as a label `job=` to any timeseries scraped from this config.
+ - job_name: 'prometheus'
+ # Override the global default and scrape targets from this job every 5 seconds.
+ scrape_interval: 5s
+ # metrics_path defaults to '/metrics'
+ # scheme defaults to 'http'.
+ static_configs:
+ - targets: ['localhost:9090']
+
+ - job_name: 'Spring Boot Application input'
+ metrics_path: '/actuator/prometheus'
+ scrape_interval: 2s
+ static_configs:
+ - targets: [ 'flightsearchapi:1133' ]
+ labels:
+ application: 'Flight SSearch Api'
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/docker-compose.yml b/jdk_21_maven/cs/rest/flight-search-api/docker-compose.yml
new file mode 100644
index 000000000..779ba549a
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/docker-compose.yml
@@ -0,0 +1,68 @@
+services:
+
+ mongodb:
+ image: "mongo:latest"
+ container_name: mongo-container
+ restart: always
+ ports:
+ - "27017:27017"
+ volumes:
+ - mongodb_data_container:/data/db
+ networks:
+ - flightsearchapinetwork
+
+
+ flightsearchapi:
+ image: 'flightsearchapi:latest'
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: flightsearchapi
+ restart: on-failure
+ env_file:
+ - .env # Use the .env file for environment variables
+ ports:
+ - "1133:1133"
+ environment:
+ - server.port=1133
+ - spring.data.mongodb.host=mongodb
+ - spring.data.mongodb.port=${MONGO_DB_PORT}
+ - spring.data.mongodb.database=${MONGO_DB_NAME}
+ depends_on:
+ - mongodb
+ networks:
+ - flightsearchapinetwork
+
+ prometheus:
+ image: prom/prometheus:latest
+ container_name: prometheus
+ restart: unless-stopped
+ ports:
+ - "9090:9090"
+ volumes:
+ - ./data/prometheus/config:/etc/prometheus/
+ command:
+ - '--config.file=/etc/prometheus/prometheus.yml'
+ networks:
+ - flightsearchapinetwork
+
+ grafana:
+ image: "grafana/grafana-oss:latest"
+ pull_policy: always
+ container_name: grafana
+ restart: unless-stopped
+ ports:
+ - "3000:3000"
+ volumes:
+ - ./data/grafana:/var/lib/grafana
+ environment:
+ - GF_SECURITY_ADMIN_PASSWORD=admin
+ - GF_SERVER_DOMAIN=localhost
+ networks:
+ - flightsearchapinetwork
+
+volumes:
+ mongodb_data_container:
+
+networks:
+ flightsearchapinetwork:
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/jenkins/Dockerfile b/jdk_21_maven/cs/rest/flight-search-api/jenkins/Dockerfile
new file mode 100644
index 000000000..5c4c50b11
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/jenkins/Dockerfile
@@ -0,0 +1,12 @@
+FROM jenkins/jenkins:lts
+
+# Plugin list
+COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
+RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt
+
+# For Groovy scripts, init.d directory
+COPY init.groovy.d/ /var/jenkins_home/init.groovy.d/
+
+# Install Docker CLI
+USER root
+RUN apt-get update && apt-get install -y docker.io
diff --git a/jdk_21_maven/cs/rest/flight-search-api/jenkins/docker-compose.yml b/jdk_21_maven/cs/rest/flight-search-api/jenkins/docker-compose.yml
new file mode 100644
index 000000000..3dc7c1cd3
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/jenkins/docker-compose.yml
@@ -0,0 +1,27 @@
+
+services:
+ jenkins:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: jenkins-server
+ ports:
+ - "8080:8080" # Expose Jenkins UI on port 8080
+ - "50000:50000" # Expose port for Jenkins agents
+ volumes:
+ - jenkins_home:/var/jenkins_home # Persistent Jenkins data
+ - /var/run/docker.sock.raw:/var/run/docker.sock # Mount Docker socket for Docker builds
+ - ../k8s:/var/jenkins_home/k8s # Mount Kubernetes configuration files (optional)
+ - ./init.groovy.d:/var/jenkins_home/init.groovy.d # Mount Jenkins init scripts (optional)
+ environment:
+ JAVA_OPTS: "-Djenkins.install.runSetupWizard=false" # Skip setup wizard (optional)
+ user: root # Run as root to allow installing dependencies
+ networks:
+ - common-network
+
+volumes:
+ jenkins_home:
+
+networks:
+ common-network:
+ driver: bridge
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/jenkins/init.groovy.d/create-pipeline.groovy b/jdk_21_maven/cs/rest/flight-search-api/jenkins/init.groovy.d/create-pipeline.groovy
new file mode 100644
index 000000000..b112c0e31
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/jenkins/init.groovy.d/create-pipeline.groovy
@@ -0,0 +1,32 @@
+import hudson.plugins.git.BranchSpec
+import hudson.plugins.git.GitSCM
+import hudson.plugins.git.UserRemoteConfig
+import jenkins.model.*
+import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition
+
+def instance = Jenkins.getInstance()
+
+def jobName = "flightsearchapi"
+def job = instance.getItem(jobName)
+
+if (job != null) {
+ job.delete()
+}
+
+def pipelineJob = instance.createProject(org.jenkinsci.plugins.workflow.job.WorkflowJob, jobName)
+def definition = new CpsScmFlowDefinition(
+ new GitSCM(
+ [
+ new UserRemoteConfig("https://github.com/Rapter1990/flightsearchapi.git", null, null, null)
+ ],
+ [new BranchSpec("*/main")],
+ false, Collections.emptyList(),
+ null, null, Collections.emptyList()
+ ),
+ "Jenkinsfile"
+)
+definition.setLightweight(true)
+pipelineJob.setDefinition(definition)
+pipelineJob.save()
+
+println("Pipeline job '${jobName}' has been successfully created!")
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/jenkins/plugins.txt b/jdk_21_maven/cs/rest/flight-search-api/jenkins/plugins.txt
new file mode 100644
index 000000000..750208391
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/jenkins/plugins.txt
@@ -0,0 +1,7 @@
+workflow-aggregator
+git
+job-dsl
+ws-cleanup
+docker-plugin
+docker-workflow
+docker-commons
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/k8s/app-config.yml b/jdk_21_maven/cs/rest/flight-search-api/k8s/app-config.yml
new file mode 100644
index 000000000..d5f3ddd8a
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/k8s/app-config.yml
@@ -0,0 +1,10 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: flightsearchapi-config
+ namespace: default
+data:
+ server.port: "1133"
+ spring.data.mongodb.host: "mongodb"
+ spring.data.mongodb.port: "27017"
+ spring.data.mongodb.database: "flightdatabase"
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/k8s/flightsearchapi-deployment.yml b/jdk_21_maven/cs/rest/flight-search-api/k8s/flightsearchapi-deployment.yml
new file mode 100644
index 000000000..381a185a8
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/k8s/flightsearchapi-deployment.yml
@@ -0,0 +1,43 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: flightsearchapi
+ namespace: default
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: flightsearchapi
+ template:
+ metadata:
+ labels:
+ app: flightsearchapi
+ spec:
+ containers:
+ - name: flightsearchapi
+ image: noyandocker/flightsearchapi:latest
+ ports:
+ - containerPort: 1133
+ envFrom:
+ - configMapRef:
+ name: flightsearchapi-config
+ volumeMounts:
+ - name: flightsearchapi-data
+ mountPath: /data/logs
+ volumes:
+ - name: flightsearchapi-data
+ emptyDir: {}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: flightsearchapi-service
+ namespace: default
+spec:
+ selector:
+ app: flightsearchapi
+ ports:
+ - protocol: TCP
+ port: 1133
+ targetPort: 1133
+ type: NodePort
diff --git a/jdk_21_maven/cs/rest/flight-search-api/k8s/grafana-deployment.yml b/jdk_21_maven/cs/rest/flight-search-api/k8s/grafana-deployment.yml
new file mode 100644
index 000000000..e0c982a32
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/k8s/grafana-deployment.yml
@@ -0,0 +1,38 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: grafana
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: grafana
+ template:
+ metadata:
+ labels:
+ app: grafana
+ spec:
+ containers:
+ - name: grafana
+ image: grafana/grafana-oss:latest
+ ports:
+ - containerPort: 3000
+ env:
+ - name: GF_SECURITY_ADMIN_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: grafana-secret
+ key: GF_SECURITY_ADMIN_PASSWORD
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: grafana-service
+spec:
+ selector:
+ app: grafana
+ ports:
+ - protocol: TCP
+ port: 3000
+ targetPort: 3000
+ type: NodePort
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/k8s/grafana-secret.yml b/jdk_21_maven/cs/rest/flight-search-api/k8s/grafana-secret.yml
new file mode 100644
index 000000000..4b1988f38
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/k8s/grafana-secret.yml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: grafana-secret
+ namespace: default
+type: Opaque
+data:
+ GF_SECURITY_ADMIN_PASSWORD: YWRtaW4= # Base64 encoded password (admin)
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/k8s/mongodb-pv.yml b/jdk_21_maven/cs/rest/flight-search-api/k8s/mongodb-pv.yml
new file mode 100644
index 000000000..6423a17ca
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/k8s/mongodb-pv.yml
@@ -0,0 +1,26 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: mongodb-pv
+ namespace: default
+spec:
+ capacity:
+ storage: 5Gi
+ accessModes:
+ - ReadWriteOnce
+ hostPath:
+ path: /data/mongodb
+
+---
+
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mongodb-pvc
+ namespace: default
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 5Gi
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/k8s/mongodb-statefulset.yml b/jdk_21_maven/cs/rest/flight-search-api/k8s/mongodb-statefulset.yml
new file mode 100644
index 000000000..197bde0c4
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/k8s/mongodb-statefulset.yml
@@ -0,0 +1,50 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: mongodb
+ namespace: default
+spec:
+ serviceName: mongodb
+ replicas: 1
+ selector:
+ matchLabels:
+ app: mongodb
+ template:
+ metadata:
+ labels:
+ app: mongodb
+ spec:
+ containers:
+ - name: mongodb
+ image: mongo:latest
+ ports:
+ - containerPort: 27017
+ volumeMounts:
+ - name: mongodb-data
+ mountPath: /data/db
+ volumeClaimTemplates:
+ - metadata:
+ name: mongodb-data
+ spec:
+ accessModes: [ "ReadWriteOnce" ]
+ resources:
+ requests:
+ storage: 5Gi
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: mongodb
+ namespace: default
+ labels:
+ app: mongodb
+spec:
+ ports:
+ - name: mongo
+ port: 27017
+ targetPort: 27017
+ clusterIP: None # Headless service for StatefulSet
+ selector:
+ app: mongodb
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/k8s/prometheus-configmap.yml b/jdk_21_maven/cs/rest/flight-search-api/k8s/prometheus-configmap.yml
new file mode 100644
index 000000000..9f51b9aea
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/k8s/prometheus-configmap.yml
@@ -0,0 +1,30 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: prometheus-config
+ namespace: default
+data:
+ prometheus.yml: |
+ global:
+ scrape_interval: 120s # By default, scrape targets every 15 seconds.
+ evaluation_interval: 120s # By default, evaluate rules every 15 seconds.
+
+ # A scrape configuration containing exactly one endpoint to scrape:
+ # Here it's Prometheus itself.
+ scrape_configs:
+ # The job name is added as a label `job=` to any timeseries scraped from this config.
+ - job_name: 'prometheus'
+ # Override the global default and scrape targets from this job every 5 seconds.
+ scrape_interval: 5s
+ # metrics_path defaults to '/metrics'
+ # scheme defaults to 'http'.
+ static_configs:
+ - targets: ['localhost:9090']
+
+ - job_name: 'Spring Boot Application input'
+ metrics_path: '/actuator/prometheus'
+ scrape_interval: 2s
+ static_configs:
+ - targets: ['flightsearchapi-service.default.svc.cluster.local:1133']
+ labels:
+ application: 'Flight Search Api'
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/k8s/prometheus-deployment.yml b/jdk_21_maven/cs/rest/flight-search-api/k8s/prometheus-deployment.yml
new file mode 100644
index 000000000..93696a821
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/k8s/prometheus-deployment.yml
@@ -0,0 +1,42 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: prometheus
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: prometheus
+ template:
+ metadata:
+ labels:
+ app: prometheus
+ spec:
+ containers:
+ - name: prometheus
+ image: prom/prometheus:latest
+ args:
+ - '--config.file=/etc/prometheus/prometheus.yml'
+ ports:
+ - containerPort: 9090
+ volumeMounts:
+ - name: prometheus-config
+ mountPath: /etc/prometheus/prometheus.yml
+ subPath: prometheus.yml # Ensure only the file is mounted
+ volumes:
+ - name: prometheus-config
+ configMap:
+ name: prometheus-config
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: prometheus-service
+spec:
+ selector:
+ app: prometheus
+ ports:
+ - protocol: TCP
+ port: 9090
+ targetPort: 9090
+ type: NodePort
\ No newline at end of file
diff --git a/jdk_21_maven/cs/rest/flight-search-api/mvnw b/jdk_21_maven/cs/rest/flight-search-api/mvnw
new file mode 100644
index 000000000..19529ddf8
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/mvnw
@@ -0,0 +1,259 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Optional ENV vars
+# -----------------
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
+ fi
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
+ fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
+ done
+ printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
+
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/jdk_21_maven/cs/rest/flight-search-api/mvnw.cmd b/jdk_21_maven/cs/rest/flight-search-api/mvnw.cmd
new file mode 100644
index 000000000..249bdf382
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/mvnw.cmd
@@ -0,0 +1,149 @@
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM
+@REM Optional ENV vars
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM ----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/jdk_21_maven/cs/rest/flight-search-api/pom.xml b/jdk_21_maven/cs/rest/flight-search-api/pom.xml
new file mode 100644
index 000000000..2ba7efd47
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/pom.xml
@@ -0,0 +1,286 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.1
+
+
+ com.case
+ flightsearchapi
+ 0.0.1-SNAPSHOT
+ flightsearchapi
+ Flight Search Api Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 21
+ 1.77
+ 0.12.3
+ 1.5.5.Final
+ 0.2.0
+ 2.1.0
+ 2.1.0
+ 0.8.12
+ 3.5.2
+ 3.5.2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+ org.bouncycastle
+ bcpkix-jdk18on
+ ${bouncycastle.version}
+
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.14.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${jsonwebtoken.version}
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jsonwebtoken.version}
+
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${jsonwebtoken.version}
+
+
+
+
+ org.mapstruct
+ mapstruct
+ ${mapstruct.version}
+
+
+
+
+ org.testcontainers
+ testcontainers
+ test
+
+
+
+ org.testcontainers
+ junit-jupiter
+ test
+
+
+
+ org.testcontainers
+ mongodb
+ test
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ ${openapi.version}
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ ${springdoc-openapi.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+ runtime
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ 21
+ 21
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ ${lombok-mapstruct-binding.version}
+
+
+ --enable-preview
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco-version}
+
+
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+
+ post-test-report
+ verify
+
+ report
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin-version}
+
+
+ **/*Test.java
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin-version}
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+
+
+
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/FlightSearchApiApplication.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/FlightSearchApiApplication.java
new file mode 100644
index 000000000..1620d957e
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/FlightSearchApiApplication.java
@@ -0,0 +1,27 @@
+package com.example.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * The main entry point for the Flight Search Api application with Couchbase integration.
+ * This class is responsible for launching the Spring Boot application, which initializes all necessary components,
+ * including configurations for Couchbase, and starts the application context.
+ * The {@link SpringApplication#run(Class, String...)} method is invoked to start the application.
+ */
+@SpringBootApplication
+public class FlightSearchApiApplication {
+
+ /**
+ * The main method which serves as the entry point to launch the Spring Boot application.
+ * This method initializes and runs the {@link SpringApplication} to start the application.
+ * It automatically configures the application context and any required components.
+ *
+ * @param args Command-line arguments passed to the application.
+ * These are forwarded to the {@link SpringApplication#run(Class, String...)} method.
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(FlightSearchApiApplication.class, args);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/config/SecurityConfig.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/config/SecurityConfig.java
new file mode 100644
index 000000000..74cc572c0
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/config/SecurityConfig.java
@@ -0,0 +1,115 @@
+package com.example.demo.auth.config;
+
+import com.example.demo.auth.filter.CustomBearerTokenAuthenticationFilter;
+import com.example.demo.auth.security.CustomAuthenticationEntryPoint;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+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.core.session.SessionRegistryImpl;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
+import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
+
+/**
+ * Configuration class for security settings of the application.
+ * This class sets up the authentication and authorization mechanisms using Spring Security.
+ * It defines the session management strategy, security filter chain, CORS configurations,
+ * and password encoding strategy.
+ */
+@Configuration
+@EnableWebSecurity
+@RequiredArgsConstructor
+@EnableMethodSecurity
+public class SecurityConfig {
+
+ /**
+ * Configures the session authentication strategy for the application.
+ * Registers a session authentication strategy that tracks authenticated sessions
+ * using a {@link SessionRegistryImpl}.
+ *
+ * @return the session authentication strategy to be used.
+ */
+ @Bean
+ protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
+ return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
+ }
+
+ /**
+ * Configures the security filter chain for handling HTTP security.
+ *
+ * @param httpSecurity the {@link HttpSecurity} to configure.
+ * @param customBearerTokenAuthenticationFilter a custom filter for handling bearer token authentication.
+ * @param customAuthenticationEntryPoint a custom entry point for authentication failures.
+ * @return the configured {@link SecurityFilterChain}.
+ * @throws Exception if an error occurs during configuration.
+ */
+ @Bean
+ public SecurityFilterChain filterChain(
+ final HttpSecurity httpSecurity,
+ final CustomBearerTokenAuthenticationFilter customBearerTokenAuthenticationFilter,
+ final CustomAuthenticationEntryPoint customAuthenticationEntryPoint
+ ) throws Exception {
+
+ httpSecurity
+ .exceptionHandling(customizer -> customizer.authenticationEntryPoint(customAuthenticationEntryPoint))
+ .cors(customizer -> customizer.configurationSource(corsConfigurationSource()))
+ .csrf(AbstractHttpConfigurer::disable)
+ .authorizeHttpRequests(customizer -> customizer
+ .requestMatchers(HttpMethod.POST, "/api/v1/authentication/**").permitAll()
+ .requestMatchers(
+ "/swagger-ui/**",
+ "/swagger-ui.html",
+ "/v2/api-docs/**",
+ "/v3/api-docs/**",
+ "/actuator/**"
+ ).permitAll()
+ .anyRequest().authenticated()
+ )
+ .sessionManagement(customizer -> customizer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .addFilterBefore(customBearerTokenAuthenticationFilter, BearerTokenAuthenticationFilter.class);
+
+ return httpSecurity.build();
+ }
+
+ /**
+ * Provides the CORS configuration for the application.
+ * Allows all origins, methods, and headers for cross-origin requests.
+ *
+ * @return the {@link CorsConfigurationSource} with the specified configurations.
+ */
+ private CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+ configuration.setAllowedOrigins(List.of("*"));
+ configuration.setAllowedMethods(List.of("*"));
+ configuration.setAllowedHeaders(List.of("*"));
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", configuration);
+ return source;
+ }
+
+ /**
+ * Provides the password encoder for the application.
+ * Configures a {@link BCryptPasswordEncoder} for securely hashing passwords.
+ *
+ * @return the {@link PasswordEncoder} to be used.
+ */
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/config/TokenConfigurationParameter.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/config/TokenConfigurationParameter.java
new file mode 100644
index 000000000..a2c7a7c3c
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/config/TokenConfigurationParameter.java
@@ -0,0 +1,63 @@
+package com.example.demo.auth.config;
+
+import com.example.demo.auth.model.enums.ConfigurationParameter;
+import com.example.demo.auth.utils.KeyConverter;
+import lombok.Getter;
+import org.springframework.context.annotation.Configuration;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+/**
+ * Configuration class for token-related parameters.
+ * This class retrieves and initializes token configuration settings, including
+ * issuer, access token expiration, refresh token expiration, public key, and private key.
+ * The parameters are loaded from predefined configuration constants.
+ */
+@Getter
+@Configuration
+public class TokenConfigurationParameter {
+
+ private final String issuer;
+ private final int accessTokenExpireMinute;
+ private final int refreshTokenExpireDay;
+ private final PublicKey publicKey;
+ private final PrivateKey privateKey;
+
+ /**
+ * Initializes the token configuration parameters.
+ *
+ * The parameters are retrieved from the {@link ConfigurationParameter} class:
+ *
+ * Key values are converted using the {@link KeyConverter} utility class.
+ *
+ */
+ public TokenConfigurationParameter() {
+
+ this.issuer = ConfigurationParameter.ISSUER.getDefaultValue();
+
+ this.accessTokenExpireMinute = Integer.parseInt(
+ ConfigurationParameter.AUTH_ACCESS_TOKEN_EXPIRE_MINUTE.getDefaultValue()
+ );
+
+ this.refreshTokenExpireDay = Integer.parseInt(
+ ConfigurationParameter.AUTH_REFRESH_TOKEN_EXPIRE_DAY.getDefaultValue()
+ );
+
+ this.publicKey = KeyConverter.convertPublicKey(
+ ConfigurationParameter.AUTH_PUBLIC_KEY.getDefaultValue()
+ );
+
+ this.privateKey = KeyConverter.convertPrivateKey(
+ ConfigurationParameter.AUTH_PRIVATE_KEY.getDefaultValue()
+ );
+
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/controller/AuthController.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/controller/AuthController.java
new file mode 100644
index 000000000..e0f4bfe35
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/controller/AuthController.java
@@ -0,0 +1,133 @@
+package com.example.demo.auth.controller;
+
+import com.example.demo.auth.model.Token;
+import com.example.demo.auth.model.dto.request.LoginRequest;
+import com.example.demo.auth.model.dto.request.RegisterRequest;
+import com.example.demo.auth.model.dto.request.TokenInvalidateRequest;
+import com.example.demo.auth.model.dto.request.TokenRefreshRequest;
+import com.example.demo.auth.model.dto.response.TokenResponse;
+import com.example.demo.auth.model.mapper.TokenToTokenResponseMapper;
+import com.example.demo.auth.service.LoginService;
+import com.example.demo.auth.service.LogoutService;
+import com.example.demo.auth.service.RefreshTokenService;
+import com.example.demo.auth.service.RegisterService;
+import com.example.demo.common.model.dto.response.CustomResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+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;
+
+/**
+ * Controller that handles user authentication and authorization operations.
+ * This controller provides endpoints for user registration, login, token refreshing, and logout functionality.
+ * It delegates the business logic to the appropriate services: {@link RegisterService}, {@link LoginService},
+ * {@link RefreshTokenService}, and {@link LogoutService}.
+ */
+@RestController
+@RequestMapping("/api/v1/authentication/user")
+@RequiredArgsConstructor
+@Validated
+@Tag(name = "Authentication", description = "Handles user authentication and authorization operations.")
+public class AuthController {
+
+ private final RegisterService registerService;
+
+ private final LoginService loginService;
+
+ private final RefreshTokenService refreshTokenService;
+
+ private final LogoutService logoutService;
+
+ private final TokenToTokenResponseMapper tokenToTokenResponseMapper = TokenToTokenResponseMapper.initialize();
+
+ /**
+ * Endpoint to register a new user.
+ *
+ * @param registerRequest The {@link RegisterRequest} object containing user registration details.
+ * @return A {@link CustomResponse} indicating the success of the registration operation.
+ */
+ @Operation(
+ summary = "Register a new user",
+ description = "Registers a new user in the system.",
+ responses = {
+ @ApiResponse(responseCode = "200", description = "User successfully registered"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data"),
+ @ApiResponse(responseCode = "409", description = "User already exists")
+ }
+ )
+ @PostMapping("/register")
+ public CustomResponse registerAdmin(@RequestBody @Valid final RegisterRequest registerRequest) {
+ registerService.registerUser(registerRequest);
+ return CustomResponse.SUCCESS;
+ }
+
+ /**
+ * Endpoint for user login.
+ *
+ * @param loginRequest The {@link LoginRequest} object containing user login credentials.
+ * @return A {@link CustomResponse} containing a {@link TokenResponse} with the generated tokens.
+ */
+ @Operation(
+ summary = "User login",
+ description = "Authenticates a user and returns an access and refresh token.",
+ responses = {
+ @ApiResponse(responseCode = "200", description = "Login successful"),
+ @ApiResponse(responseCode = "401", description = "Invalid login credentials")
+ }
+ )
+ @PostMapping("/login")
+ public CustomResponse loginAdmin(@RequestBody @Valid final LoginRequest loginRequest) {
+ final Token token = loginService.login(loginRequest);
+ final TokenResponse tokenResponse = tokenToTokenResponseMapper.map(token);
+ return CustomResponse.successOf(tokenResponse);
+ }
+
+ /**
+ * Endpoint to refresh an expired access token.
+ *
+ * @param tokenRefreshRequest The {@link TokenRefreshRequest} object containing the refresh token.
+ * @return A {@link CustomResponse} containing a {@link TokenResponse} with the refreshed tokens.
+ */
+ @Operation(
+ summary = "Refresh access token",
+ description = "Refreshes an expired access token using a valid refresh token.",
+ responses = {
+ @ApiResponse(responseCode = "200", description = "Token successfully refreshed"),
+ @ApiResponse(responseCode = "400", description = "Invalid refresh token"),
+ @ApiResponse(responseCode = "401", description = "Refresh token expired or invalid")
+ }
+ )
+ @PostMapping("/refresh-token")
+ public CustomResponse refreshToken(@RequestBody @Valid final TokenRefreshRequest tokenRefreshRequest) {
+ final Token token = refreshTokenService.refreshToken(tokenRefreshRequest);
+ final TokenResponse tokenResponse = tokenToTokenResponseMapper.map(token);
+ return CustomResponse.successOf(tokenResponse);
+ }
+
+ /**
+ * Endpoint to log out a user.
+ *
+ * @param tokenInvalidateRequest The {@link TokenInvalidateRequest} object containing the token to be invalidated.
+ * @return A {@link CustomResponse} indicating the success of the logout operation.
+ */
+ @Operation(
+ summary = "Log out a user",
+ description = "Invalidates the provided token, effectively logging out the user.",
+ responses = {
+ @ApiResponse(responseCode = "200", description = "Logout successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid token provided")
+ }
+ )
+ @PostMapping("/logout")
+ public CustomResponse logout(@RequestBody @Valid final TokenInvalidateRequest tokenInvalidateRequest) {
+ logoutService.logout(tokenInvalidateRequest);
+ return CustomResponse.SUCCESS;
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/PasswordNotValidException.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/PasswordNotValidException.java
new file mode 100644
index 000000000..b667ae404
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/PasswordNotValidException.java
@@ -0,0 +1,40 @@
+package com.example.demo.auth.exception;
+
+import org.springframework.http.HttpStatus;
+
+import java.io.Serial;
+
+/**
+ * Custom exception thrown when a password is not valid.
+ * This exception extends {@link RuntimeException} and provides a default message
+ * for invalid passwords. It also allows for custom messages to be appended to the default message.
+ */
+public class PasswordNotValidException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = -8180169011759975985L;
+
+ public static final HttpStatus STATUS = HttpStatus.BAD_REQUEST;
+
+ private static final String DEFAULT_MESSAGE = """
+ Password is not valid!
+ """;
+
+ /**
+ * Constructs a new {@code PasswordNotValidException} with the default message.
+ */
+ public PasswordNotValidException() {
+ super(DEFAULT_MESSAGE);
+ }
+
+ /**
+ * Constructs a new {@code PasswordNotValidException} with the default message
+ * and appends a custom message.
+ *
+ * @param message the custom message to append to the default message.
+ */
+ public PasswordNotValidException(final String message) {
+ super(DEFAULT_MESSAGE + " " + message);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/RoleNotFoundException.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/RoleNotFoundException.java
new file mode 100644
index 000000000..a93b3de5f
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/RoleNotFoundException.java
@@ -0,0 +1,41 @@
+package com.example.demo.auth.exception;
+
+import org.springframework.http.HttpStatus;
+
+import java.io.Serial;
+
+/**
+ * Custom exception thrown when a requested role is not found.
+ * This exception extends {@link RuntimeException} and is typically used in scenarios
+ * where a role lookup operation fails. It provides a default message and allows
+ * for an additional custom message to be appended.
+ */
+public class RoleNotFoundException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = -4353606558549954783L;
+
+ public static final HttpStatus STATUS = HttpStatus.NOT_FOUND;
+
+ private static final String DEFAULT_MESSAGE = """
+ Role not found!
+ """;
+
+ /**
+ * Constructs a new {@code RoleNotFoundException} with the default message.
+ */
+ public RoleNotFoundException() {
+ super(DEFAULT_MESSAGE);
+ }
+
+ /**
+ * Constructs a new {@code RoleNotFoundException} with the default message
+ * and appends a custom message.
+ *
+ * @param message the custom message to append to the default message.
+ */
+ public RoleNotFoundException(final String message) {
+ super(DEFAULT_MESSAGE + " " + message);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/TokenAlreadyInvalidatedException.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/TokenAlreadyInvalidatedException.java
new file mode 100644
index 000000000..be424d013
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/TokenAlreadyInvalidatedException.java
@@ -0,0 +1,40 @@
+package com.example.demo.auth.exception;
+
+import org.springframework.http.HttpStatus;
+
+import java.io.Serial;
+
+/**
+ * Custom exception thrown when an attempt is made to invalidate a token that is already invalidated.
+ * This exception extends {@link RuntimeException} and is typically used in token management
+ * scenarios to indicate that a token cannot be invalidated again.
+ */
+public class TokenAlreadyInvalidatedException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = 66846615891590622L;
+
+ public static final HttpStatus STATUS = HttpStatus.BAD_REQUEST;
+
+ private static final String DEFAULT_MESSAGE = """
+ Token is already invalidated!
+ """;
+
+ /**
+ * Constructs a new {@code TokenAlreadyInvalidatedException} with the default message.
+ */
+ public TokenAlreadyInvalidatedException() {
+ super(DEFAULT_MESSAGE);
+ }
+
+ /**
+ * Constructs a new {@code TokenAlreadyInvalidatedException} with the default message
+ * and includes the specified token ID in the message.
+ *
+ * @param tokenId the ID of the token that was already invalidated.
+ */
+ public TokenAlreadyInvalidatedException(final String tokenId) {
+ super(DEFAULT_MESSAGE + " TokenID = " + tokenId);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UnAuthorizeAttemptException.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UnAuthorizeAttemptException.java
new file mode 100644
index 000000000..82eff6ccb
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UnAuthorizeAttemptException.java
@@ -0,0 +1,31 @@
+package com.example.demo.auth.exception;
+
+import org.springframework.http.HttpStatus;
+
+import java.io.Serial;
+
+/**
+ * Custom exception thrown when an unauthorized attempt is made to perform an action,
+ * such as creating a to-do item without proper permissions.
+ * This exception extends {@link RuntimeException} and is typically used in scenarios
+ * where access control or authorization checks fail.
+ */
+public class UnAuthorizeAttemptException extends RuntimeException{
+
+ @Serial
+ private static final long serialVersionUID = 668466158918970622L;
+
+ public static final HttpStatus STATUS = HttpStatus.UNAUTHORIZED;
+
+ private static final String DEFAULT_MESSAGE = """
+ You do not have permission to create a to-do item.
+ """;
+
+ /**
+ * Constructs a new {@code UnAuthorizeAttemptException} with the default message.
+ */
+ public UnAuthorizeAttemptException() {
+ super(DEFAULT_MESSAGE);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserAlreadyExistException.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserAlreadyExistException.java
new file mode 100644
index 000000000..c82734ab5
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserAlreadyExistException.java
@@ -0,0 +1,40 @@
+package com.example.demo.auth.exception;
+
+import org.springframework.http.HttpStatus;
+
+import java.io.Serial;
+
+/**
+ * Custom exception thrown when an attempt is made to create a user that already exists.
+ * This exception extends {@link RuntimeException} and is typically used to indicate
+ * a conflict during user registration or creation.
+ */
+public class UserAlreadyExistException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = -6595921257346957788L;
+
+ public static final HttpStatus STATUS = HttpStatus.CONFLICT;
+
+ private static final String DEFAULT_MESSAGE = """
+ User already exist!
+ """;
+
+ /**
+ * Constructs a new {@code UserAlreadyExistException} with the default message.
+ */
+ public UserAlreadyExistException() {
+ super(DEFAULT_MESSAGE);
+ }
+
+ /**
+ * Constructs a new {@code UserAlreadyExistException} with the default message
+ * and appends a custom message.
+ *
+ * @param message the custom message to append to the default message.
+ */
+ public UserAlreadyExistException(final String message) {
+ super(DEFAULT_MESSAGE + " " + message);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserNotFoundException.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserNotFoundException.java
new file mode 100644
index 000000000..7683133c6
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserNotFoundException.java
@@ -0,0 +1,40 @@
+package com.example.demo.auth.exception;
+
+import org.springframework.http.HttpStatus;
+
+import java.io.Serial;
+
+/**
+ * Custom exception thrown when a requested user is not found in the system.
+ * This exception extends {@link RuntimeException} and is typically used to indicate
+ * that a user-related query has failed due to the absence of the user.
+ */
+public class UserNotFoundException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = 6032856035243860910L;
+
+ public static final HttpStatus STATUS = HttpStatus.NOT_FOUND;
+
+ private static final String DEFAULT_MESSAGE = """
+ User not found!
+ """;
+
+ /**
+ * Constructs a new {@code UserNotFoundException} with the default message.
+ */
+ public UserNotFoundException() {
+ super(DEFAULT_MESSAGE);
+ }
+
+ /**
+ * Constructs a new {@code UserNotFoundException} with the default message
+ * and appends a custom message.
+ *
+ * @param message the custom message to append to the default message.
+ */
+ public UserNotFoundException(final String message) {
+ super(DEFAULT_MESSAGE + " " + message);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserStatusNotValidException.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserStatusNotValidException.java
new file mode 100644
index 000000000..924bd42c2
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/exception/UserStatusNotValidException.java
@@ -0,0 +1,40 @@
+package com.example.demo.auth.exception;
+
+import org.springframework.http.HttpStatus;
+
+import java.io.Serial;
+
+/**
+ * Custom exception thrown when a user's status is deemed invalid for a specific operation.
+ * This exception extends {@link RuntimeException} and is typically used in scenarios
+ * where user-related validations fail due to an invalid status.
+ */
+public class UserStatusNotValidException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = -4774378445348925475L;
+
+ public static final HttpStatus STATUS = HttpStatus.BAD_REQUEST;
+
+ private static final String DEFAULT_MESSAGE = """
+ User status is not valid!
+ """;
+
+ /**
+ * Constructs a new {@code UserStatusNotValidException} with the default message.
+ */
+ public UserStatusNotValidException() {
+ super(DEFAULT_MESSAGE);
+ }
+
+ /**
+ * Constructs a new {@code UserStatusNotValidException} with the default message
+ * and appends a custom message.
+ *
+ * @param message the custom message to append to the default message.
+ */
+ public UserStatusNotValidException(final String message) {
+ super(DEFAULT_MESSAGE + " " + message);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/filter/CustomBearerTokenAuthenticationFilter.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/filter/CustomBearerTokenAuthenticationFilter.java
new file mode 100644
index 000000000..84bdfb2c6
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/filter/CustomBearerTokenAuthenticationFilter.java
@@ -0,0 +1,74 @@
+package com.example.demo.auth.filter;
+
+import com.example.demo.auth.model.Token;
+import com.example.demo.auth.service.InvalidTokenService;
+import com.example.demo.auth.service.TokenService;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpHeaders;
+import org.springframework.lang.NonNull;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+/**
+ * Custom filter for handling Bearer token authentication.
+ * This filter extracts the Bearer token from the HTTP `Authorization` header, validates the token,
+ * checks if it has been invalidated, and sets the authentication in the {@link SecurityContextHolder}.
+ * Extends {@link OncePerRequestFilter} to ensure the filter is executed only once per request.
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class CustomBearerTokenAuthenticationFilter extends OncePerRequestFilter {
+
+ private final TokenService tokenService;
+ private final InvalidTokenService invalidTokenService;
+
+ /**
+ * Performs filtering logic for each HTTP request to validate Bearer tokens.
+ *
+ * @param httpServletRequest the current HTTP request.
+ * @param httpServletResponse the current HTTP response.
+ * @param filterChain the filter chain to delegate further request processing.
+ * @throws ServletException if an error occurs during request processing.
+ * @throws IOException if an input or output error occurs during request processing.
+ */
+ @Override
+ protected void doFilterInternal(@NonNull final HttpServletRequest httpServletRequest,
+ @NonNull final HttpServletResponse httpServletResponse,
+ @NonNull final FilterChain filterChain) throws ServletException, IOException {
+
+ log.debug("API Request was secured with Security!");
+
+ final String authorizationHeader = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION);
+
+ if (Token.isBearerToken(authorizationHeader)) {
+
+ final String jwt = Token.getJwt(authorizationHeader);
+
+ tokenService.verifyAndValidate(jwt);
+
+ final String tokenId = tokenService.getId(jwt);
+
+ invalidTokenService.checkForInvalidityOfToken(tokenId);
+
+ final UsernamePasswordAuthenticationToken authentication = tokenService
+ .getAuthentication(jwt);
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+
+ }
+
+ filterChain.doFilter(httpServletRequest,httpServletResponse);
+
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/Token.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/Token.java
new file mode 100644
index 000000000..c41b43221
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/Token.java
@@ -0,0 +1,44 @@
+package com.example.demo.auth.model;
+
+import lombok.Builder;
+import lombok.Getter;
+import org.springframework.util.StringUtils;
+
+/**
+ * Represents an access and refresh token used for authentication and authorization.
+ * This class contains information about the access token, its expiration time, and the refresh token.
+ * It also provides utility methods for handling tokens in the form of "Bearer" tokens, which are commonly used
+ * in HTTP Authorization headers.
+ */
+@Getter
+@Builder
+public class Token {
+
+ private String accessToken;
+ private Long accessTokenExpiresAt;
+ private String refreshToken;
+
+ private static final String TOKEN_PREFIX = "Bearer ";
+
+ /**
+ * Checks if the given authorization header contains a valid bearer token.
+ *
+ * @param authorizationHeader The authorization header to be checked.
+ * @return {@code true} if the authorization header contains a valid bearer token, otherwise {@code false}.
+ */
+ public static boolean isBearerToken(final String authorizationHeader) {
+ return StringUtils.hasText(authorizationHeader) &&
+ authorizationHeader.startsWith(TOKEN_PREFIX);
+ }
+
+ /**
+ * Extracts the JWT token from the authorization header.
+ *
+ * @param authorizationHeader The authorization header containing the token.
+ * @return The JWT token extracted from the authorization header.
+ */
+ public static String getJwt(final String authorizationHeader) {
+ return authorizationHeader.replace(TOKEN_PREFIX, "");
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/User.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/User.java
new file mode 100644
index 000000000..498710765
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/User.java
@@ -0,0 +1,32 @@
+package com.example.demo.auth.model;
+
+import com.example.demo.auth.model.enums.UserStatus;
+import com.example.demo.auth.model.enums.UserType;
+import com.example.demo.common.model.BaseDomainModel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * Represents an access and refresh token used for authentication and authorization.
+ * This class contains information about the access token, its expiration time, and the refresh token.
+ * It also provides utility methods for handling tokens in the form of "Bearer" tokens, which are commonly used
+ * in HTTP Authorization headers.
+ */
+@Getter
+@Setter
+@SuperBuilder
+@EqualsAndHashCode(callSuper = true)
+public class User extends BaseDomainModel {
+
+ private String id;
+ private String email;
+ private String firstName;
+ private String lastName;
+ private String phoneNumber;
+ private UserType userType;
+ private UserStatus userStatus;
+
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/LoginRequest.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/LoginRequest.java
new file mode 100644
index 000000000..3c9ac0153
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/LoginRequest.java
@@ -0,0 +1,24 @@
+package com.example.demo.auth.model.dto.request;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Represents a request for user login containing email and password.
+ * This class is used to capture login credentials when a user attempts to log in.
+ * It is validated to ensure that both the email and password fields are not blank.
+ */
+@Getter
+@Setter
+@Builder
+public class LoginRequest {
+
+ @NotBlank
+ private String email;
+
+ @NotBlank
+ private String password;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/RegisterRequest.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/RegisterRequest.java
new file mode 100644
index 000000000..bc89eed95
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/RegisterRequest.java
@@ -0,0 +1,42 @@
+package com.example.demo.auth.model.dto.request;
+
+import com.example.demo.auth.model.enums.UserType;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.*;
+
+/**
+ * Represents a request for user registration containing the necessary details
+ * to create a new user in the system.
+ * This class is used to capture the data required for registering a user, including
+ * their email, password, first and last name, phone number, and user type. It also
+ * enforces validation rules on the fields to ensure data integrity.
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class RegisterRequest {
+
+ @Email(message = "Please enter a valid e-mail address")
+ @Size(min = 7, message = "Minimum e-mail length is 7 characters.")
+ private String email;
+
+ @Size(min = 8, message = "Minimum password length is 8 characters.")
+ private String password;
+
+ @NotBlank(message = "First name can't be blank.")
+ private String firstName;
+
+ @NotBlank(message = "Last name can't be blank.")
+ private String lastName;
+
+ @NotBlank(message = "Phone number can't be blank.")
+ @Size(min = 11, max = 20, message = "Phone number must be between 11 and 20 characters.")
+ private String phoneNumber;
+
+ private UserType userType;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/TokenInvalidateRequest.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/TokenInvalidateRequest.java
new file mode 100644
index 000000000..f611e48cd
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/TokenInvalidateRequest.java
@@ -0,0 +1,25 @@
+package com.example.demo.auth.model.dto.request;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+
+/**
+ * Represents a request to invalidate a token, typically used to log out a user
+ * by invalidating their access and refresh tokens.
+ * This class is used to capture the access token and refresh token that need to be invalidated.
+ * Both tokens are required for the invalidation process to be carried out.
+ */
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TokenInvalidateRequest {
+
+ @NotBlank
+ private String accessToken;
+
+ @NotBlank
+ private String refreshToken;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/TokenRefreshRequest.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/TokenRefreshRequest.java
new file mode 100644
index 000000000..96174a688
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/request/TokenRefreshRequest.java
@@ -0,0 +1,21 @@
+package com.example.demo.auth.model.dto.request;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+
+/**
+ * Represents a request to refresh a token, typically used when a user needs a new access token
+ * using a valid refresh token.
+ * This class captures the refresh token, which is required for generating a new access token.
+ */
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class TokenRefreshRequest {
+
+ @NotBlank
+ private String refreshToken;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/response/TokenResponse.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/response/TokenResponse.java
new file mode 100644
index 000000000..f24113f13
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/dto/response/TokenResponse.java
@@ -0,0 +1,21 @@
+package com.example.demo.auth.model.dto.response;
+
+import lombok.*;
+
+/**
+ * Represents a request to refresh a token, typically used when a user needs a new access token
+ * using a valid refresh token.
+ * This class captures the refresh token, which is required for generating a new access token.
+ */
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TokenResponse {
+
+ private String accessToken;
+ private Long accessTokenExpiresAt;
+ private String refreshToken;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/entity/InvalidTokenEntity.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/entity/InvalidTokenEntity.java
new file mode 100644
index 000000000..2d77f4afe
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/entity/InvalidTokenEntity.java
@@ -0,0 +1,32 @@
+package com.example.demo.auth.model.entity;
+
+import com.example.demo.common.model.entity.BaseEntity;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+/**
+ * Represents an entity for storing invalidated tokens in MongoDB.
+ * Each token is uniquely identified by {@code tokenId}.
+ * Extends {@link BaseEntity} for common timestamp fields.
+ */
+@Getter
+@Setter
+@SuperBuilder
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@AllArgsConstructor
+@Document(collection = "invalid-token-collection")
+public class InvalidTokenEntity extends BaseEntity {
+
+ @Id
+ @Indexed(unique = true)
+ private String id;
+
+ @Field(name = "TOKEN_ID")
+ private String tokenId;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/entity/UserEntity.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/entity/UserEntity.java
new file mode 100644
index 000000000..082475de1
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/entity/UserEntity.java
@@ -0,0 +1,80 @@
+package com.example.demo.auth.model.entity;
+
+import com.example.demo.auth.model.enums.TokenClaims;
+import com.example.demo.auth.model.enums.UserStatus;
+import com.example.demo.auth.model.enums.UserType;
+import com.example.demo.common.model.entity.BaseEntity;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents a user entity in the system, storing personal and authentication details.
+ * Mapped to the MongoDB {@code user-collection}, with {@code id} as the unique identifier.
+ * Extends {@link BaseEntity}, inheriting common properties like timestamps.
+ */
+@Getter
+@Setter
+@SuperBuilder
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@AllArgsConstructor
+@Document(collection = "user-collection")
+public class UserEntity extends BaseEntity {
+
+ @Id
+ @Indexed(unique = true)
+ private String id;
+
+ @Field(name = "EMAIL")
+ @Indexed(unique = true)
+ private String email;
+
+ @Field(name = "PASSWORD")
+ private String password;
+
+ @Field(name = "FIRST_NAME")
+ private String firstName;
+
+ @Field(name = "LAST_NAME")
+ private String lastName;
+
+ @Field(name = "PHONE_NUMBER")
+ private String phoneNumber;
+
+ @Field(name = "USER_TYPE")
+ private UserType userType;
+
+ @Builder.Default
+ @Field(name = "USER_STATUS")
+ private UserStatus userStatus = UserStatus.ACTIVE;
+
+ /**
+ * Generates a map of claims associated with the user.
+ * This method creates a map of key-value pairs representing the user's claims,
+ * which can be used for JWT token generation or other purposes where user-related
+ * data needs to be included.
+ *
+ * @return a map containing the user's claims.
+ */
+ public Map getClaims() {
+ final Map claims = new HashMap<>();
+
+ claims.put(TokenClaims.USER_ID.getValue(), this.id);
+ claims.put(TokenClaims.USER_TYPE.getValue(), this.userType);
+ claims.put(TokenClaims.USER_STATUS.getValue(), this.userStatus);
+ claims.put(TokenClaims.USER_FIRST_NAME.getValue(), this.firstName);
+ claims.put(TokenClaims.USER_LAST_NAME.getValue(), this.lastName);
+ claims.put(TokenClaims.USER_EMAIL.getValue(), this.email);
+ claims.put(TokenClaims.USER_PHONE_NUMBER.getValue(), this.phoneNumber);
+
+ return claims;
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/ConfigurationParameter.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/ConfigurationParameter.java
new file mode 100644
index 000000000..fe64138fe
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/ConfigurationParameter.java
@@ -0,0 +1,63 @@
+package com.example.demo.auth.model.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Enum representing the configuration parameters used in the application.
+ * Each parameter corresponds to a specific configuration value, such as issuer, token expiration times,
+ * and keys used for authentication and encryption.
+ */
+@Getter
+@RequiredArgsConstructor
+public enum ConfigurationParameter {
+
+ ISSUER("ISSUER"),
+
+ AUTH_ACCESS_TOKEN_EXPIRE_MINUTE("30"),
+ AUTH_REFRESH_TOKEN_EXPIRE_DAY("1"),
+ AUTH_PUBLIC_KEY("""
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1HmZ3A379M6Rv9UnMt9R
+ Wq0a6bpcnoOWJxTi2exwnecW3r1X1PjeUvsDogy7RYjhlxU0G+1r38gPWfUW2FNd
+ tsa3H+FDhJ6dcNc4uKVYPsiVJukHi4NrvWA8E8dPdLW1lNcijr4PqXjvZTLoS1QX
+ f30wnNLBNDwdPXTESodi/n87VoSH2ChgLZUVfoS3m/NlUN8Z58gGxRcpUyjl+MmC
+ hD2cfyWr2xdxKd+UQMrd36LfyoWh0IlONlxo0H5x8JIwlziLbPEAh7dJ9QYM0b5G
+ msXBAzrILvW5+POSq4u1vNlzSwdLe+AZ6bnCwQVrqvMn/I7JT+4+lY8BsP/gMRQL
+ awIDAQAB
+ -----END PUBLIC KEY-----
+ """),
+ AUTH_PRIVATE_KEY("""
+ -----BEGIN PRIVATE KEY-----
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDUeZncDfv0zpG/
+ 1Scy31FarRrpulyeg5YnFOLZ7HCd5xbevVfU+N5S+wOiDLtFiOGXFTQb7WvfyA9Z
+ 9RbYU122xrcf4UOEnp1w1zi4pVg+yJUm6QeLg2u9YDwTx090tbWU1yKOvg+peO9l
+ MuhLVBd/fTCc0sE0PB09dMRKh2L+fztWhIfYKGAtlRV+hLeb82VQ3xnnyAbFFylT
+ KOX4yYKEPZx/JavbF3Ep35RAyt3fot/KhaHQiU42XGjQfnHwkjCXOIts8QCHt0n1
+ BgzRvkaaxcEDOsgu9bn485Kri7W82XNLB0t74BnpucLBBWuq8yf8jslP7j6VjwGw
+ /+AxFAtrAgMBAAECggEARWpw5N7AuQsfvN+DjfA9oPU6/K9BARyWWrBNKMtBQ6Uy
+ 6JRNdKvV3qBZYIDuUdpVcUmhG5qmipbOxSH4U7ZwwH0NaOHscBBt+WanBlQmj2Ry
+ riKlr2PBOD6Pghq0j7mp2DWs+ZuIfGKhO5u1Hp8bijA5SJLmQg19tA1I79xpcCFC
+ flY8WaBKJCTKtH3sisS9IqAtGx3+O2fwzk5oSA6DDuX+45zTLS1vlucwhTUTDE4j
+ UitYM5CnW9LzJ72ofWZBE29twECXQa+7YYXYniqUOzYZVhy7OiwYT9HH2fDGurmL
+ F4adtcmVS6CUM6OYSV8z1DXbwLcidTqwiAj9uZ8suQKBgQD2hb/Bimgoh0dT6q9N
+ pORikLW9L/6q3TuAfNJrrP63dwgooeCcHdk8Wh1zFkNibmfdhHQM5Hu3pQq0oxtG
+ XiH1WYhluDAjyq6ahvYasgGtdzh279VcicleP9x6B6tmWop+a9kM+Yh2r3Yb5KXr
+ ZyFWLeoEkCyeXdVr1tds119Q/QKBgQDcpMKXIAiayZmcHU03pNrDGhs7m2B18S6Z
+ WcXSQRf1d98wbzzfsrbHM9k8gX5VGvZb+rwl95SyevT1LSaGM44On4WXjCUlpUnb
+ 8+mKONra8yXuWutB1aclPQddj4HAFGikJyBP45e/SXNTBPrK5cm/HULbgd76FxHv
+ QQZ2EbSOhwKBgA5pHR98LsCHv+So6Fx6khss6GLJxnJIgmztXwOKVk11ONXfOJkH
+ qaY8glIy7/d2Cr5JOttyE8VVcX3Dtxly8Ts9Y5rGnJHLDE/eKc6/rxdry7IwLOG+
+ 8DWBOCst/Zf7HPNs7IA0qgR+F0JkKErNeYZnIrHnl6QeShaGtYsYP+slAoGBANRU
+ yd5dOWqb73NIz3Jo9w0iJmrqT52wh8OTnMeFVOUogmQ96Drt5O82eiu8AjMsS0Cg
+ vkdbRoGryefXl2c2XdK8uPbqKyVbNwSwaWJW7GYf77S9UgB89ujjHh9vZtHN0hWG
+ gZXf07yFlrGh7Scsk0WThy9uf4H0iZHQ5cLhrvwpAoGAAj3uW/aAJDhfHHVgBGhC
+ la8OzEqY8UbqMYrXKfrmYOzvFYKWd5BgrZvgINhSmjxMeil952gHVsH/td+KUyqe
+ dxTAgV9WVn8KVz6KoEKJV5CarN0IOl5fZVJz1i0Q5t6VdUXLC6teh4ByrEAlsRgi
+ h2UWcQCA6KqrmJbR2q6yj+s=
+ -----END PRIVATE KEY-----
+ """);
+
+ private final String defaultValue;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/TokenClaims.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/TokenClaims.java
new file mode 100644
index 000000000..25d34641e
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/TokenClaims.java
@@ -0,0 +1,31 @@
+package com.example.demo.auth.model.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Enum representing various claims used in a JWT token.
+ * Each enum constant represents a claim, along with its associated key (string value) used in the token.
+ * These claims are typically used to store information about the user, token metadata, or other context-specific data.
+ */
+@Getter
+@RequiredArgsConstructor
+public enum TokenClaims {
+
+ JWT_ID("jti"),
+ USER_ID("userId"),
+ USER_TYPE("userType"),
+ USER_STATUS("userStatus"),
+ USER_FIRST_NAME("userFirstName"),
+ USER_LAST_NAME("userLastName"),
+ USER_EMAIL("userEmail"),
+ USER_PHONE_NUMBER("userPhoneNumber"),
+ STORE_TITLE("storeTitle"),
+ ISSUED_AT("iat"),
+ EXPIRES_AT("exp"),
+ ALGORITHM("alg"),
+ TYP("typ");
+
+ private final String value;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/TokenType.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/TokenType.java
new file mode 100644
index 000000000..a3c7f8299
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/TokenType.java
@@ -0,0 +1,18 @@
+package com.example.demo.auth.model.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Enum representing the different types of tokens that can be used in authentication schemes.
+ * Currently, only the "Bearer" token type is supported, which is commonly used in OAuth2 and JWT authentication.
+ */
+@Getter
+@RequiredArgsConstructor
+public enum TokenType {
+
+ BEARER("Bearer");
+
+ private final String value;
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/UserStatus.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/UserStatus.java
new file mode 100644
index 000000000..9bca7043c
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/UserStatus.java
@@ -0,0 +1,11 @@
+package com.example.demo.auth.model.enums;
+
+/**
+ * Enum representing the different statuses a user can have in the system.
+ * This can be used to track and manage user account states in the application.
+ */
+public enum UserStatus {
+ ACTIVE,
+ PASSIVE,
+ SUSPENDED
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/UserType.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/UserType.java
new file mode 100644
index 000000000..3cd5d80a2
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/enums/UserType.java
@@ -0,0 +1,10 @@
+package com.example.demo.auth.model.enums;
+
+/**
+ * Enum representing the different types of users in the system.
+ * This is used to differentiate between regular users and administrative users.
+ */
+public enum UserType {
+ USER,
+ ADMIN
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/RegisterRequestToUserEntityMapper.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/RegisterRequestToUserEntityMapper.java
new file mode 100644
index 000000000..8ad742edb
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/RegisterRequestToUserEntityMapper.java
@@ -0,0 +1,45 @@
+package com.example.demo.auth.model.mapper;
+
+import com.example.demo.auth.model.dto.request.RegisterRequest;
+import com.example.demo.auth.model.entity.UserEntity;
+import com.example.demo.common.model.mapper.BaseMapper;
+import org.mapstruct.Mapper;
+import org.mapstruct.Named;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * Mapper interface for converting {@link RegisterRequest} to {@link UserEntity}.
+ * This interface extends {@link BaseMapper} and provides the mapping logic from the
+ * {@link RegisterRequest} DTO to a {@link UserEntity} entity. It includes a custom mapping method
+ * for saving the {@link RegisterRequest} to the {@link UserEntity}.
+ */
+@Mapper
+public interface RegisterRequestToUserEntityMapper extends BaseMapper {
+
+ /**
+ * Maps a {@link RegisterRequest} to a {@link UserEntity} for saving purposes.
+ *
+ * @param registerRequest The {@link RegisterRequest} object to be mapped.
+ * @return A {@link UserEntity} populated with the data from the given {@link RegisterRequest}.
+ */
+ @Named("mapForSaving")
+ default UserEntity mapForSaving(RegisterRequest registerRequest) {
+ return UserEntity.builder()
+ .email(registerRequest.getEmail())
+ .firstName(registerRequest.getFirstName())
+ .lastName(registerRequest.getLastName())
+ .phoneNumber(registerRequest.getPhoneNumber())
+ .userType(registerRequest.getUserType())
+ .build();
+ }
+
+ /**
+ * Initializes and returns an instance of {@link RegisterRequestToUserEntityMapper}.
+ *
+ * @return An initialized {@link RegisterRequestToUserEntityMapper} instance.
+ */
+ static RegisterRequestToUserEntityMapper initialize() {
+ return Mappers.getMapper(RegisterRequestToUserEntityMapper.class);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/TokenToTokenResponseMapper.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/TokenToTokenResponseMapper.java
new file mode 100644
index 000000000..3c182ae26
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/TokenToTokenResponseMapper.java
@@ -0,0 +1,38 @@
+package com.example.demo.auth.model.mapper;
+
+import com.example.demo.auth.model.Token;
+import com.example.demo.auth.model.dto.response.TokenResponse;
+import com.example.demo.common.model.mapper.BaseMapper;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * Mapper interface for converting {@link Token} to {@link TokenResponse}.
+ * This interface extends {@link BaseMapper} and provides the mapping logic from the
+ * {@link Token} entity to a {@link TokenResponse} DTO. It includes a method for mapping a
+ * {@link Token} to a {@link TokenResponse}.
+ */
+@Mapper
+public interface TokenToTokenResponseMapper extends BaseMapper {
+
+ /**
+ * Maps a {@link Token} to a {@link TokenResponse}.
+ *
+ * @param source The {@link Token} object to be mapped.
+ * @return A {@link TokenResponse} object containing the mapped data from the {@link Token}.
+ */
+ @Override
+ TokenResponse map(Token source);
+
+ /**
+ * Initializes and returns an instance of {@link TokenToTokenResponseMapper}.
+ * This method provides a way to retrieve the singleton mapper instance for
+ * converting {@link Token} to {@link TokenResponse}.
+ *
+ * @return An initialized {@link TokenToTokenResponseMapper} instance.
+ */
+ static TokenToTokenResponseMapper initialize() {
+ return Mappers.getMapper(TokenToTokenResponseMapper.class);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/UserEntityToUserMapper.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/UserEntityToUserMapper.java
new file mode 100644
index 000000000..3420a01d1
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/model/mapper/UserEntityToUserMapper.java
@@ -0,0 +1,36 @@
+package com.example.demo.auth.model.mapper;
+
+import com.example.demo.auth.model.User;
+import com.example.demo.auth.model.entity.UserEntity;
+import com.example.demo.common.model.mapper.BaseMapper;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * Mapper interface for converting {@link UserEntity} to {@link User}.
+ * This interface extends {@link BaseMapper} and provides the mapping logic from the
+ * {@link UserEntity} entity to a {@link User} DTO. It includes a method for mapping a
+ * {@link UserEntity} to a {@link User}.
+ */
+@Mapper
+public interface UserEntityToUserMapper extends BaseMapper {
+
+ /**
+ * Maps a {@link UserEntity} to a {@link User}.
+ *
+ * @param source The {@link UserEntity} object to be mapped.
+ * @return A {@link User} object containing the mapped data from the {@link UserEntity}.
+ */
+ @Override
+ User map(UserEntity source);
+
+ /**
+ * Initializes and returns an instance of {@link UserEntityToUserMapper}.
+ *
+ * @return An initialized {@link UserEntityToUserMapper} instance.
+ */
+ static UserEntityToUserMapper initialize() {
+ return Mappers.getMapper(UserEntityToUserMapper.class);
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/repository/InvalidTokenRepository.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/repository/InvalidTokenRepository.java
new file mode 100644
index 000000000..c8a0609a4
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/repository/InvalidTokenRepository.java
@@ -0,0 +1,24 @@
+package com.example.demo.auth.repository;
+
+import com.example.demo.auth.model.entity.InvalidTokenEntity;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+import java.util.Optional;
+
+/**
+ * Repository interface for managing {@link InvalidTokenEntity} objects in Couchbase.
+ * This interface extends {@link MongoRepository} to provide CRUD operations for the {@link InvalidTokenEntity}.
+ * It also includes custom query methods to interact with the data store.
+ */
+public interface InvalidTokenRepository extends MongoRepository {
+
+ /**
+ * Finds an {@link InvalidTokenEntity} by its token ID.
+ * This method is used to retrieve an invalid token entity based on the unique token ID.
+ *
+ * @param tokenId The unique ID of the token to search for.
+ * @return An {@link Optional} containing the {@link InvalidTokenEntity} if found, or {@link Optional#empty()} if not found.
+ */
+ Optional findByTokenId(final String tokenId);
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/repository/UserRepository.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/repository/UserRepository.java
new file mode 100644
index 000000000..df7f490c6
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/repository/UserRepository.java
@@ -0,0 +1,33 @@
+package com.example.demo.auth.repository;
+
+import com.example.demo.auth.model.entity.UserEntity;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+import java.util.Optional;
+
+/**
+ * Repository interface for managing {@link UserEntity} objects in Couchbase.
+ * This interface extends {@link MongoRepository} to provide CRUD operations for the {@link UserEntity}.
+ * It also includes custom query methods to interact with the data store related to user entities.
+ */
+public interface UserRepository extends MongoRepository {
+
+ /**
+ * Checks whether a {@link UserEntity} exists in the database with the specified email.
+ * This method is used to verify if a user with the given email already exists in the database.
+ *
+ * @param email The email address to check for the existence of a user.
+ * @return {@code true} if a user with the specified email exists, otherwise {@code false}.
+ */
+ boolean existsUserEntityByEmail(final String email);
+
+ /**
+ * Finds a {@link UserEntity} by its email address.
+ * This method is used to retrieve a user entity based on the unique email address.
+ *
+ * @param email The email address of the user to search for.
+ * @return An {@link Optional} containing the {@link UserEntity} if found, or {@link Optional#empty()} if not found.
+ */
+ Optional findUserEntityByEmail(final String email);
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/security/CustomAuthenticationEntryPoint.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/security/CustomAuthenticationEntryPoint.java
new file mode 100644
index 000000000..405629fb7
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/security/CustomAuthenticationEntryPoint.java
@@ -0,0 +1,64 @@
+package com.example.demo.auth.security;
+
+import com.example.demo.common.model.CustomError;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.text.DateFormat;
+
+/**
+ * Custom implementation of the {@link AuthenticationEntryPoint} interface to handle unauthorized access (HTTP 401).
+ * This class provides a custom entry point for authentication exceptions. When an authentication exception occurs,
+ * it generates a custom error response with a 401 Unauthorized status code and a specific error message format.
+ */
+@Component
+public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ static {
+ OBJECT_MAPPER.registerModule(new JavaTimeModule());
+ }
+
+ /**
+ * Handles authentication errors by sending a custom error response to the client.
+ * This method is invoked when an authentication exception occurs. It sets the response content type to
+ * JSON, status code to 401 (Unauthorized), and writes a custom error response with error details.
+ *
+ * @param httpServletRequest The {@link HttpServletRequest} containing the client request data.
+ * @param httpServletResponse The {@link HttpServletResponse} used to send the error response.
+ * @param authenticationException The {@link AuthenticationException} that caused the entry point to be triggered.
+ * @throws IOException If an I/O error occurs while writing the response.
+ */
+ @Override
+ public void commence(final HttpServletRequest httpServletRequest,
+ final HttpServletResponse httpServletResponse,
+ final AuthenticationException authenticationException) throws IOException {
+
+ httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
+
+ final CustomError customError = CustomError.builder()
+ .header(CustomError.Header.AUTH_ERROR.getName())
+ .httpStatus(HttpStatus.UNAUTHORIZED)
+ .isSuccess(false)
+ .build();
+
+ final String responseBody = OBJECT_MAPPER
+ .writer(DateFormat.getDateInstance())
+ .writeValueAsString(customError);
+
+ httpServletResponse.getOutputStream()
+ .write(responseBody.getBytes());
+
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/InvalidTokenService.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/InvalidTokenService.java
new file mode 100644
index 000000000..637a30202
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/InvalidTokenService.java
@@ -0,0 +1,30 @@
+package com.example.demo.auth.service;
+
+import java.util.Set;
+
+/**
+ * Service interface for managing and validating invalidated tokens.
+ * This service is responsible for invalidating tokens and checking the validity of tokens.
+ * It provides methods to mark tokens as invalid and to check if a given token has already been invalidated.
+ */
+public interface InvalidTokenService {
+
+ /**
+ * Invalidates the tokens with the given token IDs.
+ * This method marks the tokens as invalid, making them unusable for authentication or authorization.
+ * It can be used to invalidate tokens that have been revoked or are no longer valid.
+ *
+ * @param tokenIds A set of token IDs to invalidate.
+ */
+ void invalidateTokens(final Set tokenIds);
+
+ /**
+ * Checks if the given token ID is invalidated.
+ * This method checks whether a specific token has been invalidated and is no longer valid for use.
+ * It is typically used to validate if a token is still active before allowing access to protected resources.
+ *
+ * @param tokenId The token ID to check for invalidity.
+ */
+ void checkForInvalidityOfToken(final String tokenId);
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/LoginService.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/LoginService.java
new file mode 100644
index 000000000..3c4233915
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/LoginService.java
@@ -0,0 +1,23 @@
+package com.example.demo.auth.service;
+
+import com.example.demo.auth.model.Token;
+import com.example.demo.auth.model.dto.request.LoginRequest;
+
+/**
+ * Service interface for handling user login functionality.
+ * This interface defines the method responsible for logging in a user by validating their credentials
+ * and generating a token for subsequent authentication requests.
+ */
+public interface LoginService {
+
+ /**
+ * Authenticates a user and generates an authentication token.
+ * This method accepts login credentials, validates the user, and returns a token if the login is successful.
+ * The token can then be used for authenticated access to protected resources.
+ *
+ * @param loginRequest The request containing the login credentials (e.g., username/email and password).
+ * @return A {@link Token} containing the authentication information, including access and refresh tokens.
+ */
+ Token login(final LoginRequest loginRequest);
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/LogoutService.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/LogoutService.java
new file mode 100644
index 000000000..8f8503ceb
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/LogoutService.java
@@ -0,0 +1,21 @@
+package com.example.demo.auth.service;
+
+import com.example.demo.auth.model.dto.request.TokenInvalidateRequest;
+
+/**
+ * Service interface for handling user logout functionality.
+ * This interface defines the method responsible for logging out a user by invalidating their token.
+ * It ensures that the user's authentication token is no longer valid, preventing further access to protected resources.
+ */
+public interface LogoutService {
+
+ /**
+ * Logs out a user by invalidating their authentication token.
+ * This method accepts a request containing the token information to be invalidated. Once the token is invalidated,
+ * it can no longer be used for authentication, ensuring that the user is effectively logged out.
+ *
+ * @param tokenInvalidateRequest The request containing the token to be invalidated for logging out the user.
+ */
+ void logout(final TokenInvalidateRequest tokenInvalidateRequest);
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/RefreshTokenService.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/RefreshTokenService.java
new file mode 100644
index 000000000..a87a31b7e
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/RefreshTokenService.java
@@ -0,0 +1,23 @@
+package com.example.demo.auth.service;
+
+import com.example.demo.auth.model.Token;
+import com.example.demo.auth.model.dto.request.TokenRefreshRequest;
+
+/**
+ * Service interface for handling the refreshing of authentication tokens.
+ * This interface defines the method responsible for refreshing the user's authentication token.
+ * It generates a new access token and possibly a new refresh token, typically used when the old access token has expired.
+ */
+public interface RefreshTokenService {
+
+ /**
+ * Refreshes the user's authentication token.
+ * This method accepts a request containing the refresh token and generates new access and refresh tokens.
+ * The new tokens can be used for subsequent requests that require authentication.
+ *
+ * @param tokenRefreshRequest The request containing the refresh token information to generate new tokens.
+ * @return A {@link Token} containing the new access and refresh tokens.
+ */
+ Token refreshToken(final TokenRefreshRequest tokenRefreshRequest);
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/RegisterService.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/RegisterService.java
new file mode 100644
index 000000000..8b6011f9e
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/RegisterService.java
@@ -0,0 +1,23 @@
+package com.example.demo.auth.service;
+
+import com.example.demo.auth.model.User;
+import com.example.demo.auth.model.dto.request.RegisterRequest;
+
+/**
+ * Service interface for handling user registration functionality.
+ * This interface defines the method responsible for registering a new user in the system.
+ * It processes the registration request, validates the data, and creates a new user entity.
+ */
+public interface RegisterService {
+
+ /**
+ * Registers a new user in the system.
+ * This method accepts a registration request containing the user details (e.g., email, password, etc.).
+ * It validates the input and creates a new user in the system.
+ *
+ * @param registerRequest The request containing the user's registration details (e.g., name, email, password).
+ * @return A {@link User} object representing the newly created user.
+ */
+ User registerUser(final RegisterRequest registerRequest);
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/TokenService.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/TokenService.java
new file mode 100644
index 000000000..a373ffaf6
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/TokenService.java
@@ -0,0 +1,96 @@
+package com.example.demo.auth.service;
+
+import com.example.demo.auth.model.Token;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Service interface for handling JWT token-related operations.
+ * This interface defines methods for generating, validating, and extracting information from JWT tokens.
+ * It provides functionality to create tokens, verify their validity, retrieve claims, and authenticate users.
+ */
+public interface TokenService {
+
+ /**
+ * Generates a new authentication token based on the provided claims.
+ * This method creates a new JWT token using the specified claims.
+ * The claims typically contain user-specific information such as the username, roles, and other custom attributes.
+ *
+ * @param claims The claims to be included in the JWT token.
+ * @return A {@link Token} object containing the generated JWT token.
+ */
+ Token generateToken(final Map claims);
+
+ /**
+ * Generates a new authentication token based on the provided claims and a refresh token.
+ * This method creates a new JWT token using the specified claims and includes the provided refresh token as part of the payload.
+ * It is typically used when refreshing the user's authentication token.
+ *
+ * @param claims The claims to be included in the JWT token.
+ * @param refreshToken The refresh token to include in the generated JWT token.
+ * @return A {@link Token} object containing the generated JWT token.
+ */
+ Token generateToken(final Map claims, final String refreshToken);
+
+ /**
+ * Extracts the authentication information from the provided JWT token.
+ * This method parses the JWT token and returns an {@link UsernamePasswordAuthenticationToken} containing the user authentication information.
+ * It is typically used to authenticate the user during the request processing.
+ *
+ * @param token The JWT token to extract the authentication information from.
+ * @return A {@link UsernamePasswordAuthenticationToken} containing the extracted authentication information.
+ */
+ UsernamePasswordAuthenticationToken getAuthentication(final String token);
+
+ /**
+ * Verifies the validity of the provided JWT token.
+ * This method checks if the provided JWT token is valid, including verifying its signature, expiration, and other factors.
+ *
+ * @param jwt The JWT token to verify.
+ * @throws SecurityException If the token is invalid or cannot be verified.
+ */
+ void verifyAndValidate(final String jwt);
+
+ /**
+ * Verifies the validity of a set of JWT tokens.
+ * This method checks the validity of each JWT token in the provided set,
+ * ensuring that all tokens are valid and have not been tampered with.
+ *
+ * @param jwts The set of JWT tokens to verify.
+ * @throws SecurityException If any of the tokens are invalid or cannot be verified.
+ */
+ void verifyAndValidate(final Set jwts);
+
+ /**
+ * Extracts the claims from the provided JWT token.
+ * This method parses the JWT token and retrieves the claims contained in the token.
+ * Claims typically contain user-specific information.
+ *
+ * @param jwt The JWT token to extract claims from.
+ * @return A {@link Jws} object containing the claims of the token.
+ */
+ Jws getClaims(final String jwt);
+
+ /**
+ * Retrieves the payload (claims) from the provided JWT token.
+ * This method extracts the claims (payload) from the JWT token and returns them as a {@link Claims} object.
+ *
+ * @param jwt The JWT token to retrieve the payload from.
+ * @return A {@link Claims} object containing the payload of the token.
+ */
+ Claims getPayload(final String jwt);
+
+ /**
+ * Retrieves the ID from the provided JWT token.
+ * This method extracts the unique identifier (ID) from the JWT token, typically representing the user or session.
+ *
+ * @param jwt The JWT token to retrieve the ID from.
+ * @return The unique identifier (ID) from the JWT token.
+ */
+ String getId(final String jwt);
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/InvalidTokenServiceImpl.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/InvalidTokenServiceImpl.java
new file mode 100644
index 000000000..732d6f1cf
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/InvalidTokenServiceImpl.java
@@ -0,0 +1,59 @@
+package com.example.demo.auth.service.impl;
+
+import com.example.demo.auth.exception.TokenAlreadyInvalidatedException;
+import com.example.demo.auth.model.entity.InvalidTokenEntity;
+import com.example.demo.auth.repository.InvalidTokenRepository;
+import com.example.demo.auth.service.InvalidTokenService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Service implementation for managing and validating invalidated tokens.
+ * This service is responsible for invalidating tokens and checking the validity of tokens.
+ * It provides methods to mark tokens as invalid and to check if a given token has already been invalidated.
+ */
+@Service
+@RequiredArgsConstructor
+public class InvalidTokenServiceImpl implements InvalidTokenService {
+
+ private final InvalidTokenRepository invalidTokenRepository;
+
+ /**
+ * Invalidates the tokens with the given token IDs.
+ * This method marks the tokens as invalid, making them unusable for authentication or authorization.
+ * It can be used to invalidate tokens that have been revoked or are no longer valid.
+ *
+ * @param tokenIds A set of token IDs to invalidate.
+ */
+ @Override
+ public void invalidateTokens(Set tokenIds) {
+ final Set invalidTokenEntities = tokenIds.stream()
+ .map(tokenId -> InvalidTokenEntity.builder()
+ .tokenId(tokenId)
+ .build()
+ )
+ .collect(Collectors.toSet());
+
+ invalidTokenRepository.saveAll(invalidTokenEntities);
+ }
+
+ /**
+ * Checks if the given token ID is invalidated.
+ * This method checks whether a specific token has been invalidated and is no longer valid for use.
+ * It is typically used to validate if a token is still active before allowing access to protected resources.
+ *
+ * @param tokenId The token ID to check for invalidity.
+ */
+ @Override
+ public void checkForInvalidityOfToken(String tokenId) {
+ final boolean isTokenInvalid = invalidTokenRepository.findByTokenId(tokenId).isPresent();
+
+ if (isTokenInvalid) {
+ throw new TokenAlreadyInvalidatedException(tokenId);
+ }
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/LoginServiceImpl.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/LoginServiceImpl.java
new file mode 100644
index 000000000..9a4e340a9
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/LoginServiceImpl.java
@@ -0,0 +1,54 @@
+package com.example.demo.auth.service.impl;
+
+import com.example.demo.auth.exception.PasswordNotValidException;
+import com.example.demo.auth.exception.UserNotFoundException;
+import com.example.demo.auth.model.Token;
+import com.example.demo.auth.model.dto.request.LoginRequest;
+import com.example.demo.auth.model.entity.UserEntity;
+import com.example.demo.auth.repository.UserRepository;
+import com.example.demo.auth.service.LoginService;
+import com.example.demo.auth.service.TokenService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+/**
+ * Service implementation for handling user login functionality.
+ * This interface defines the method responsible for logging in a user by validating their credentials
+ * and generating a token for subsequent authentication requests.
+ */
+@Service
+@RequiredArgsConstructor
+public class LoginServiceImpl implements LoginService {
+
+ private final UserRepository userRepository;
+ private final PasswordEncoder passwordEncoder;
+ private final TokenService tokenService;
+
+ /**
+ * Authenticates a user and generates an authentication token.
+ * This method accepts login credentials, validates the user, and returns a token if the login is successful.
+ * The token can then be used for authenticated access to protected resources.
+ *
+ * @param loginRequest The request containing the login credentials (e.g., username/email and password).
+ * @return A {@link Token} containing the authentication information, including access and refresh tokens.
+ */
+ @Override
+ public Token login(LoginRequest loginRequest) {
+
+ final UserEntity userEntityFromDB = userRepository
+ .findUserEntityByEmail(loginRequest.getEmail())
+ .orElseThrow(
+ () -> new UserNotFoundException(loginRequest.getEmail())
+ );
+
+ if (Boolean.FALSE.equals(passwordEncoder.matches(
+ loginRequest.getPassword(), userEntityFromDB.getPassword()))) {
+ throw new PasswordNotValidException();
+ }
+
+ return tokenService.generateToken(userEntityFromDB.getClaims());
+
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/LogoutServiceImpl.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/LogoutServiceImpl.java
new file mode 100644
index 000000000..6f1df25dd
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/LogoutServiceImpl.java
@@ -0,0 +1,58 @@
+package com.example.demo.auth.service.impl;
+
+import com.example.demo.auth.model.dto.request.TokenInvalidateRequest;
+import com.example.demo.auth.service.InvalidTokenService;
+import com.example.demo.auth.service.LogoutService;
+import com.example.demo.auth.service.TokenService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.Set;
+
+/**
+ * Service implementation for handling user logout functionality.
+ * This interface defines the method responsible for logging out a user by invalidating their token.
+ * It ensures that the user's authentication token is no longer valid, preventing further access to protected resources.
+ */
+@Service
+@RequiredArgsConstructor
+public class LogoutServiceImpl implements LogoutService {
+
+ private final TokenService tokenService;
+ private final InvalidTokenService invalidTokenService;
+
+ /**
+ * Logs out a user by invalidating their authentication token.
+ * This method accepts a request containing the token information to be invalidated. Once the token is invalidated,
+ * it can no longer be used for authentication, ensuring that the user is effectively logged out.
+ *
+ * @param tokenInvalidateRequest The request containing the token to be invalidated for logging out the user.
+ */
+ @Override
+ public void logout(TokenInvalidateRequest tokenInvalidateRequest) {
+
+ tokenService.verifyAndValidate(
+ Set.of(
+ tokenInvalidateRequest.getAccessToken(),
+ tokenInvalidateRequest.getRefreshToken()
+ )
+ );
+
+ final String accessTokenId = tokenService
+ .getPayload(tokenInvalidateRequest.getAccessToken())
+ .getId();
+
+ invalidTokenService.checkForInvalidityOfToken(accessTokenId);
+
+
+ final String refreshTokenId = tokenService
+ .getPayload(tokenInvalidateRequest.getRefreshToken())
+ .getId();
+
+ invalidTokenService.checkForInvalidityOfToken(refreshTokenId);
+
+ invalidTokenService.invalidateTokens(Set.of(accessTokenId,refreshTokenId));
+
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/RefreshTokenServiceImpl.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/RefreshTokenServiceImpl.java
new file mode 100644
index 000000000..0f3395eef
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/RefreshTokenServiceImpl.java
@@ -0,0 +1,71 @@
+package com.example.demo.auth.service.impl;
+
+import com.example.demo.auth.exception.UserNotFoundException;
+import com.example.demo.auth.exception.UserStatusNotValidException;
+import com.example.demo.auth.model.Token;
+import com.example.demo.auth.model.dto.request.TokenRefreshRequest;
+import com.example.demo.auth.model.entity.UserEntity;
+import com.example.demo.auth.model.enums.TokenClaims;
+import com.example.demo.auth.model.enums.UserStatus;
+import com.example.demo.auth.repository.UserRepository;
+import com.example.demo.auth.service.RefreshTokenService;
+import com.example.demo.auth.service.TokenService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * Service implementation for handling the refreshing of authentication tokens.
+ * This interface defines the method responsible for refreshing the user's authentication token.
+ * It generates a new access token and possibly a new refresh token, typically used when the old access token has expired.
+ */
+@Service
+@RequiredArgsConstructor
+public class RefreshTokenServiceImpl implements RefreshTokenService {
+
+ private final UserRepository userRepository;
+ private final TokenService tokenService;
+
+ /**
+ * Refreshes the user's authentication token.
+ * This method accepts a request containing the refresh token and generates new access and refresh tokens.
+ * The new tokens can be used for subsequent requests that require authentication.
+ *
+ * @param tokenRefreshRequest The request containing the refresh token information to generate new tokens.
+ * @return A {@link Token} containing the new access and refresh tokens.
+ */
+ @Override
+ public Token refreshToken(TokenRefreshRequest tokenRefreshRequest) {
+ tokenService.verifyAndValidate(tokenRefreshRequest.getRefreshToken());
+
+ final String adminId = tokenService
+ .getPayload(tokenRefreshRequest.getRefreshToken())
+ .get(TokenClaims.USER_ID.getValue())
+ .toString();
+
+ final UserEntity userEntityFromDB = userRepository
+ .findById(adminId)
+ .orElseThrow(UserNotFoundException::new);
+
+ this.validateAdminStatus(userEntityFromDB);
+
+ return tokenService.generateToken(
+ userEntityFromDB.getClaims(),
+ tokenRefreshRequest.getRefreshToken()
+ );
+ }
+
+ /**
+ * Validates the user's status to ensure they are active.
+ * This method checks if the user's status is set to active.
+ * If the user is not active, it throws a {@link UserStatusNotValidException}.
+ *
+ * @param userEntity The {@link UserEntity} to validate.
+ * @throws UserStatusNotValidException If the user is not in an active status.
+ */
+ private void validateAdminStatus(final UserEntity userEntity) {
+ if (!(UserStatus.ACTIVE.equals(userEntity.getUserStatus()))) {
+ throw new UserStatusNotValidException("UserStatus = " + userEntity.getUserStatus());
+ }
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/RegisterServiceImpl.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/RegisterServiceImpl.java
new file mode 100644
index 000000000..3254cbc47
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/RegisterServiceImpl.java
@@ -0,0 +1,58 @@
+package com.example.demo.auth.service.impl;
+
+import com.example.demo.auth.exception.UserAlreadyExistException;
+import com.example.demo.auth.model.User;
+import com.example.demo.auth.model.dto.request.RegisterRequest;
+import com.example.demo.auth.model.entity.UserEntity;
+import com.example.demo.auth.model.mapper.RegisterRequestToUserEntityMapper;
+import com.example.demo.auth.model.mapper.UserEntityToUserMapper;
+import com.example.demo.auth.repository.UserRepository;
+import com.example.demo.auth.service.RegisterService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+/**
+ * Service implementation for handling user registration functionality.
+ * This interface defines the method responsible for registering a new user in the system.
+ * It processes the registration request, validates the data, and creates a new user entity.
+ */
+@Service
+@RequiredArgsConstructor
+public class RegisterServiceImpl implements RegisterService {
+
+ private final UserRepository userRepository;
+
+ private final RegisterRequestToUserEntityMapper registerRequestToUserEntityMapper =
+ RegisterRequestToUserEntityMapper.initialize();
+
+ private final UserEntityToUserMapper userEntityToUserMapper = UserEntityToUserMapper.initialize();
+
+ private final PasswordEncoder passwordEncoder;
+
+ /**
+ * Registers a new user in the system.
+ * This method accepts a registration request containing the user details (e.g., email, password, etc.).
+ * It validates the input and creates a new user in the system.
+ *
+ * @param registerRequest The request containing the user's registration details (e.g., name, email, password).
+ * @return A {@link User} object representing the newly created user.
+ */
+ @Override
+ public User registerUser(final RegisterRequest registerRequest) {
+
+ if (userRepository.existsUserEntityByEmail(registerRequest.getEmail())) {
+ throw new UserAlreadyExistException("The email is already used for another user : " + registerRequest.getEmail());
+ }
+
+ final UserEntity userEntityToBeSaved = registerRequestToUserEntityMapper.mapForSaving(registerRequest);
+
+ userEntityToBeSaved.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
+
+ final UserEntity savedUserEntity = userRepository.save(userEntityToBeSaved);
+
+ return userEntityToUserMapper.map(savedUserEntity);
+
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/TokenServiceImpl.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/TokenServiceImpl.java
new file mode 100644
index 000000000..c5bee482f
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/service/impl/TokenServiceImpl.java
@@ -0,0 +1,253 @@
+package com.example.demo.auth.service.impl;
+
+import com.example.demo.auth.config.TokenConfigurationParameter;
+import com.example.demo.auth.model.Token;
+import com.example.demo.auth.model.enums.ConfigurationParameter;
+import com.example.demo.auth.model.enums.TokenClaims;
+import com.example.demo.auth.model.enums.TokenType;
+import com.example.demo.auth.model.enums.UserType;
+import com.example.demo.auth.service.InvalidTokenService;
+import com.example.demo.auth.service.TokenService;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwsHeader;
+import io.jsonwebtoken.Jwts;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * Service implementation for handling JWT token-related operations.
+ * This interface defines methods for generating, validating, and extracting information from JWT tokens.
+ * It provides functionality to create tokens, verify their validity, retrieve claims, and authenticate users.
+ */
+@Service
+@RequiredArgsConstructor
+public class TokenServiceImpl implements TokenService {
+
+ private final TokenConfigurationParameter tokenConfigurationParameter;
+ private final InvalidTokenService invalidTokenService;
+
+ /**
+ * Generates a new authentication token based on the provided claims.
+ * This method creates a new JWT token using the specified claims.
+ * The claims typically contain user-specific information such as the username, roles, and other custom attributes.
+ *
+ * @param claims The claims to be included in the JWT token.
+ * @return A {@link Token} object containing the generated JWT token.
+ */
+ @Override
+ public Token generateToken(Map claims) {
+
+ final long currentTimeMillis = System.currentTimeMillis();
+
+ final Date tokenIssuedAt = new Date(currentTimeMillis);
+
+ final Date accessTokenExpiresAt = DateUtils.addMinutes(
+ new Date(currentTimeMillis),
+ tokenConfigurationParameter.getAccessTokenExpireMinute()
+ );
+
+ final String accessToken = Jwts.builder()
+ .header()
+ .type(TokenType.BEARER.getValue())
+ .and()
+ .id(UUID.randomUUID().toString())
+ .issuer(ConfigurationParameter.ISSUER.getDefaultValue())
+ .issuedAt(tokenIssuedAt)
+ .expiration(accessTokenExpiresAt)
+ .signWith(tokenConfigurationParameter.getPrivateKey())
+ .claims(claims)
+ .compact();
+
+ final Date refreshTokenExpiresAt = DateUtils.addDays(
+ new Date(currentTimeMillis),
+ tokenConfigurationParameter.getRefreshTokenExpireDay()
+ );
+
+ final String refreshToken = Jwts.builder()
+ .header()
+ .type(TokenType.BEARER.getValue())
+ .and()
+ .id(UUID.randomUUID().toString())
+ .issuer(tokenConfigurationParameter.getIssuer())
+ .issuedAt(tokenIssuedAt)
+ .expiration(refreshTokenExpiresAt)
+ .signWith(tokenConfigurationParameter.getPrivateKey())
+ .claim(TokenClaims.USER_ID.getValue(), claims.get(TokenClaims.USER_ID.getValue()))
+ .compact();
+
+ return Token.builder()
+ .accessToken(accessToken)
+ .accessTokenExpiresAt(accessTokenExpiresAt.toInstant().getEpochSecond())
+ .refreshToken(refreshToken)
+ .build();
+ }
+
+ /**
+ * Generates a new authentication token based on the provided claims and a refresh token.
+ * This method creates a new JWT token using the specified claims and includes the provided refresh token as part of the payload.
+ * It is typically used when refreshing the user's authentication token.
+ *
+ * @param claims The claims to be included in the JWT token.
+ * @param refreshToken The refresh token to include in the generated JWT token.
+ * @return A {@link Token} object containing the generated JWT token.
+ */
+ @Override
+ public Token generateToken(Map claims, String refreshToken) {
+
+ final long currentTimeMillis = System.currentTimeMillis();
+
+ final String refreshTokenId = this.getId(refreshToken);
+
+ invalidTokenService.checkForInvalidityOfToken(refreshTokenId);
+
+ final Date accessTokenIssuedAt = new Date(currentTimeMillis);
+
+ final Date accessTokenExpiresAt = DateUtils.addMinutes(
+ new Date(currentTimeMillis),
+ tokenConfigurationParameter.getAccessTokenExpireMinute()
+ );
+
+ final String accessToken = Jwts.builder()
+ .header()
+ .type(TokenType.BEARER.getValue())
+ .and()
+ .id(UUID.randomUUID().toString())
+ .issuer(tokenConfigurationParameter.getIssuer())
+ .issuedAt(accessTokenIssuedAt)
+ .expiration(accessTokenExpiresAt)
+ .signWith(tokenConfigurationParameter.getPrivateKey())
+ .claims(claims)
+ .compact();
+
+ return Token.builder()
+ .accessToken(accessToken)
+ .accessTokenExpiresAt(accessTokenExpiresAt.toInstant().getEpochSecond())
+ .refreshToken(refreshToken)
+ .build();
+ }
+
+ /**
+ * Extracts the authentication information from the provided JWT token.
+ * This method parses the JWT token and returns an {@link UsernamePasswordAuthenticationToken} containing the user authentication information.
+ * It is typically used to authenticate the user during the request processing.
+ *
+ * @param token The JWT token to extract the authentication information from.
+ * @return A {@link UsernamePasswordAuthenticationToken} containing the extracted authentication information.
+ */
+ @Override
+ public UsernamePasswordAuthenticationToken getAuthentication(String token) {
+
+ final Jws claimsJws = Jwts.parser()
+ .verifyWith(tokenConfigurationParameter.getPublicKey())
+ .build()
+ .parseSignedClaims(token);
+
+ final JwsHeader jwsHeader = claimsJws.getHeader();
+ final Claims payload = claimsJws.getPayload();
+
+ final Jwt jwt = new org.springframework.security.oauth2.jwt.Jwt(
+ token,
+ payload.getIssuedAt().toInstant(),
+ payload.getExpiration().toInstant(),
+ Map.of(
+ TokenClaims.TYP.getValue(), jwsHeader.getType(),
+ TokenClaims.ALGORITHM.getValue(), jwsHeader.getAlgorithm()
+ ),
+ payload
+ );
+
+
+ final UserType userType = UserType.valueOf(payload.get(TokenClaims.USER_TYPE.getValue()).toString());
+
+ final List authorities = new ArrayList<>();
+ authorities.add(new SimpleGrantedAuthority(userType.name()));
+
+ return UsernamePasswordAuthenticationToken
+ .authenticated(jwt, null, authorities);
+ }
+
+ /**
+ * Verifies the validity of the provided JWT token.
+ * This method checks if the provided JWT token is valid, including verifying its signature, expiration, and other factors.
+ *
+ * @param jwt The JWT token to verify.
+ * @throws SecurityException If the token is invalid or cannot be verified.
+ */
+ @Override
+ public void verifyAndValidate(String jwt) {
+ Jwts.parser()
+ .verifyWith(tokenConfigurationParameter.getPublicKey())
+ .build()
+ .parseSignedClaims(jwt);
+ }
+
+ /**
+ * Verifies the validity of a set of JWT tokens.
+ * This method checks the validity of each JWT token in the provided set,
+ * ensuring that all tokens are valid and have not been tampered with.
+ *
+ * @param jwts The set of JWT tokens to verify.
+ * @throws SecurityException If any of the tokens are invalid or cannot be verified.
+ */
+ @Override
+ public void verifyAndValidate(Set jwts) {
+ jwts.forEach(this::verifyAndValidate);
+ }
+
+ /**
+ * Extracts the claims from the provided JWT token.
+ * This method parses the JWT token and retrieves the claims contained in the token.
+ * Claims typically contain user-specific information.
+ *
+ * @param jwt The JWT token to extract claims from.
+ * @return A {@link Jws} object containing the claims of the token.
+ */
+ @Override
+ public Jws getClaims(String jwt) {
+ return Jwts.parser()
+ .verifyWith(tokenConfigurationParameter.getPublicKey())
+ .build()
+ .parseSignedClaims(jwt);
+ }
+
+ /**
+ * Retrieves the payload (claims) from the provided JWT token.
+ * This method extracts the claims (payload) from the JWT token and returns them as a {@link Claims} object.
+ *
+ * @param jwt The JWT token to retrieve the payload from.
+ * @return A {@link Claims} object containing the payload of the token.
+ */
+ @Override
+ public Claims getPayload(String jwt) {
+ return Jwts.parser()
+ .verifyWith(tokenConfigurationParameter.getPublicKey())
+ .build()
+ .parseSignedClaims(jwt)
+ .getPayload();
+ }
+
+ /**
+ * Retrieves the ID from the provided JWT token.
+ * This method extracts the unique identifier (ID) from the JWT token, typically representing the user or session.
+ *
+ * @param jwt The JWT token to retrieve the ID from.
+ * @return The unique identifier (ID) from the JWT token.
+ */
+ @Override
+ public String getId(String jwt) {
+ return Jwts.parser()
+ .verifyWith(tokenConfigurationParameter.getPublicKey())
+ .build()
+ .parseSignedClaims(jwt)
+ .getPayload()
+ .getId();
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/utils/KeyConverter.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/utils/KeyConverter.java
new file mode 100644
index 000000000..f134440f6
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/auth/utils/KeyConverter.java
@@ -0,0 +1,66 @@
+package com.example.demo.auth.utils;
+
+import lombok.experimental.UtilityClass;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+/**
+ * Utility class for converting PEM-encoded public and private keys to {@link PublicKey} and {@link PrivateKey} objects.
+ * This class provides methods to convert PEM-encoded key strings to their respective {@link PublicKey} and {@link PrivateKey} objects
+ * using the BouncyCastle library.
+ */
+@UtilityClass
+public class KeyConverter {
+
+ /**
+ * Converts a PEM-encoded public key string to a {@link PublicKey} object.
+ * This method parses a PEM-encoded public key string, extracts the public key information, and returns the corresponding
+ * {@link PublicKey} object.
+ *
+ * @param publicPemKey The PEM-encoded public key string.
+ * @return The corresponding {@link PublicKey} object.
+ * @throws RuntimeException if there is an error reading or parsing the PEM key.
+ */
+ public PublicKey convertPublicKey(final String publicPemKey) {
+
+ final StringReader keyReader = new StringReader(publicPemKey);
+ try {
+ SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo
+ .getInstance(new PEMParser(keyReader).readObject());
+ return new JcaPEMKeyConverter().getPublicKey(publicKeyInfo);
+ } catch (IOException exception) {
+ throw new RuntimeException(exception);
+ }
+
+ }
+
+ /**
+ * Converts a PEM-encoded private key string to a {@link PrivateKey} object.
+ * This method parses a PEM-encoded private key string, extracts the private key information, and returns the corresponding
+ * {@link PrivateKey} object.
+ *
+ * @param privatePemKey The PEM-encoded private key string.
+ * @return The corresponding {@link PrivateKey} object.
+ * @throws RuntimeException if there is an error reading or parsing the PEM key.
+ */
+ public PrivateKey convertPrivateKey(final String privatePemKey) {
+
+ StringReader keyReader = new StringReader(privatePemKey);
+ try {
+ PrivateKeyInfo privateKeyInfo = PrivateKeyInfo
+ .getInstance(new PEMParser(keyReader).readObject());
+ return new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo);
+ } catch (IOException exception) {
+ throw new RuntimeException(exception);
+ }
+
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/audit/BaseEntityListener.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/audit/BaseEntityListener.java
new file mode 100644
index 000000000..91059ed4d
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/audit/BaseEntityListener.java
@@ -0,0 +1,83 @@
+package com.example.demo.common.audit;
+
+import com.example.demo.auth.model.enums.TokenClaims;
+import com.example.demo.common.model.entity.BaseEntity;
+import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
+import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Field;
+import java.time.LocalDateTime;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * Listener for MongoDB lifecycle events to manage auditing fields in {@link BaseEntity}.
+ */
+@Component
+public class BaseEntityListener extends AbstractMongoEventListener {
+
+ /**
+ * Sets auditing fields before converting the entity to a MongoDB document.
+ *
+ * @param event the event triggered before entity conversion.
+ */
+ @Override
+ public void onBeforeConvert(BeforeConvertEvent event) {
+ BaseEntity entity = event.getSource();
+
+ // Set ID for entities that have it
+ setEntityIdIfNeeded(entity);
+
+ if (entity.getCreatedAt() == null) {
+ entity.setCreatedAt(LocalDateTime.now());
+ entity.setCreatedBy(getCurrentUser());
+ }
+
+ // Always update 'updatedAt' and 'updatedBy'
+ entity.setUpdatedAt(LocalDateTime.now());
+ entity.setUpdatedBy(getCurrentUser());
+
+ }
+
+
+ /**
+ * Checks if the entity has an 'id' field and sets it to a new UUID if it is null or empty.
+ *
+ * @param entity the entity to check and modify
+ */
+ private void setEntityIdIfNeeded(BaseEntity entity) {
+ try {
+ // Use reflection to check if the entity has an 'id' field
+ Field idField = entity.getClass().getDeclaredField("id");
+ idField.setAccessible(true); // Make the field accessible
+
+ // If the field is null or empty, set it to a new UUID
+ Object idValue = idField.get(entity);
+ if (idValue == null || idValue.toString().isEmpty()) {
+ idField.set(entity, UUID.randomUUID().toString());
+ }
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ // If no 'id' field is found, or it can't be accessed, ignore (entity may not have an 'id' field)
+ }
+ }
+
+ /**
+ * Retrieves the email of the currently authenticated user from the security context.
+ * If no authenticated user is found, it returns "anonymousUser".
+ *
+ * @return the email of the current user or "anonymousUser" if not authenticated
+ */
+ private String getCurrentUser() {
+ return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
+ .map(Authentication::getPrincipal)
+ .filter(user -> !"anonymousUser".equals(user))
+ .map(Jwt.class::cast)
+ .map(jwt -> jwt.getClaim(TokenClaims.USER_EMAIL.getValue()).toString())
+ .orElse("anonymousUser");
+ }
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/config/MongoConfig.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/config/MongoConfig.java
new file mode 100644
index 000000000..b11f3a03e
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/config/MongoConfig.java
@@ -0,0 +1,13 @@
+package com.example.demo.common.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.mongodb.config.EnableMongoAuditing;
+
+/**
+ * Configuration class to enable auditing for MongoDB entities.
+ */
+@Configuration
+@EnableMongoAuditing
+public class MongoConfig {
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/config/OpenApiConfig.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/config/OpenApiConfig.java
new file mode 100644
index 000000000..e1a807ccf
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/config/OpenApiConfig.java
@@ -0,0 +1,36 @@
+package com.example.demo.common.config;
+
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
+import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
+import io.swagger.v3.oas.annotations.info.Contact;
+import io.swagger.v3.oas.annotations.info.Info;
+import io.swagger.v3.oas.annotations.security.SecurityScheme;
+
+/**
+ * Configuration class for setting up OpenAPI documentation for the application.
+ * This class configures the metadata and security settings for the API documentation using OpenAPI 3.0 annotations.
+ */
+@OpenAPIDefinition(
+ info = @Info(
+ contact = @Contact(
+ name = "Sercan Noyan Germiyanoğlu",
+ url = "https://github.com/Rapter1990/flightsearchapi"
+ ),
+ description = "Case Study - Flight Search Api" +
+ " (Java 21, Spring Boot, Mongodb, JUnit, Spring Security, JWT, Docker, Kubernetes, Prometheus, Grafana, Github Actions (CI/CD) ) ",
+ title = "flightsearchapi",
+ version = "1.0.0"
+ )
+)
+@SecurityScheme(
+ name = "bearerAuth",
+ description = "JWT Token",
+ scheme = "bearer",
+ type = SecuritySchemeType.HTTP,
+ bearerFormat = "JWT",
+ in = SecuritySchemeIn.HEADER
+)
+public class OpenApiConfig {
+
+}
diff --git a/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/exception/GlobalExceptionHandler.java b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/exception/GlobalExceptionHandler.java
new file mode 100644
index 000000000..eabf3848a
--- /dev/null
+++ b/jdk_21_maven/cs/rest/flight-search-api/src/main/java/com/example/demo/common/exception/GlobalExceptionHandler.java
@@ -0,0 +1,310 @@
+package com.example.demo.common.exception;
+
+import com.example.demo.auth.exception.*;
+import com.example.demo.common.model.CustomError;
+import com.example.demo.flight.exception.AirportNameAlreadyExistException;
+import com.example.demo.flight.exception.AirportNotFoundException;
+import jakarta.validation.ConstraintViolationException;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Global exception handler for handling various types of exceptions across the application.
+ * This class provides centralized error handling for the entire application and returns custom error responses.
+ */
+@RestControllerAdvice
+class GlobalExceptionHandler {
+
+ /**
+ * Handles MethodArgumentNotValidException, which is thrown when method arguments are not valid.
+ * The response contains details about the validation errors for each field.
+ *
+ * @param ex The MethodArgumentNotValidException that is thrown.
+ * @return ResponseEntity containing the custom error message and validation details.
+ */
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ protected ResponseEntity