Repository: tlandeka/authentication-microservice-with-domain-driven-design
Branch: main
Commit: a7cfd82824d9
Files: 209
Total size: 254.6 KB
Directory structure:
gitextract_3sq3ne0n/
├── .github/
│ └── workflows/
│ └── maven.yml
├── .gitignore
├── .mvn/
│ └── wrapper/
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── 1-init.sql
├── Dockerfile
├── LICENSE
├── README.md
├── ddd_common-v1.0.0.jar
├── docker-compose-ci.yml
├── docker-compose.yml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── tomo/
│ │ └── mcauthentication/
│ │ ├── McAuthenticationApplication.java
│ │ ├── application/
│ │ │ ├── BaseMapper.java
│ │ │ ├── McAuthenticationEventHandler.java
│ │ │ ├── authentication/
│ │ │ │ ├── BaseLoginCommandHandler.java
│ │ │ │ ├── EmailLoginCommandHandler.java
│ │ │ │ ├── FacebookLoginCommandHandler.java
│ │ │ │ ├── GoogleLoginCommandHandler.java
│ │ │ │ ├── LogoutCommandHandler.java
│ │ │ │ ├── SessionAuthenticationCommandHandler.java
│ │ │ │ ├── command/
│ │ │ │ │ ├── BaseLoginCommand.java
│ │ │ │ │ ├── EmailLoginCommand.java
│ │ │ │ │ ├── FacebookLoginCommand.java
│ │ │ │ │ ├── GoogleLoginCommand.java
│ │ │ │ │ ├── LogoutCommand.java
│ │ │ │ │ └── SessionAuthenticationCommand.java
│ │ │ │ └── dto/
│ │ │ │ ├── RecoveryPasswordDto.java
│ │ │ │ └── SessionDto.java
│ │ │ ├── configuration/
│ │ │ │ ├── AbstractVoidyCommandHandler.java
│ │ │ │ ├── CommandHandler.java
│ │ │ │ ├── QueryHandler.java
│ │ │ │ └── RequestHandler.java
│ │ │ ├── contracts/
│ │ │ │ ├── BaseCommand.java
│ │ │ │ ├── BaseQuery.java
│ │ │ │ ├── BaseRequest.java
│ │ │ │ ├── Command.java
│ │ │ │ ├── Identifiable.java
│ │ │ │ ├── McAuthenticationModule.java
│ │ │ │ ├── Query.java
│ │ │ │ ├── Request.java
│ │ │ │ ├── Response.java
│ │ │ │ ├── Voidy.java
│ │ │ │ └── security/
│ │ │ │ ├── AbstractAuthenticateCommand.java
│ │ │ │ ├── AbstractAuthenticateQuery.java
│ │ │ │ ├── AbstractAuthenticateRequest.java
│ │ │ │ ├── AbstractAuthorizeCommand.java
│ │ │ │ ├── AbstractAuthorizeQuery.java
│ │ │ │ ├── AbstractAuthorizeRequest.java
│ │ │ │ ├── Authenticate.java
│ │ │ │ └── Authorize.java
│ │ │ ├── recovery/
│ │ │ │ ├── CreatePasswordRecoveryCodeCommandHandler.java
│ │ │ │ ├── GetUserRegistrationWithRecoveryCodeQueryHandler.java
│ │ │ │ ├── PasswordRecoveryCodeCreatedEventHandler.java
│ │ │ │ ├── SendPasswordRecoveryEmailCommandHandler.java
│ │ │ │ ├── UpdatePasswordWithRecoveryCodeCommandHandler.java
│ │ │ │ ├── command/
│ │ │ │ │ ├── CreatePasswordRecoveryCodeCommand.java
│ │ │ │ │ ├── SendPasswordRecoveryEmailCommand.java
│ │ │ │ │ └── UpdatePasswordWithRecoveryCodeCommand.java
│ │ │ │ └── dto/
│ │ │ │ └── GetUserRegistrationWithRecoveryCodeQuery.java
│ │ │ ├── registration/
│ │ │ │ ├── ChangePasswordCommandHandler.java
│ │ │ │ ├── ConfirmUserRegistrationCommandHandler.java
│ │ │ │ ├── GetUserRegistrationQueryHandler.java
│ │ │ │ ├── NewUserRegisteredEventHandler.java
│ │ │ │ ├── RegisterNewUserCommandHandler.java
│ │ │ │ ├── SendRegistrationConfirmationEmailCommandHandler.java
│ │ │ │ ├── command/
│ │ │ │ │ ├── ChangePasswordCommand.java
│ │ │ │ │ ├── ConfirmUserRegistrationCommand.java
│ │ │ │ │ ├── RegisterNewUserCommand.java
│ │ │ │ │ └── SendRegistrationConfirmationEmailCommand.java
│ │ │ │ ├── dto/
│ │ │ │ │ └── UserRegistrationDto.java
│ │ │ │ └── query/
│ │ │ │ └── GetUserRegistrationQuery.java
│ │ │ └── users/
│ │ │ ├── ChangeUserDetailsCommandHandler.java
│ │ │ ├── GetUserQueryHandler.java
│ │ │ ├── command/
│ │ │ │ └── ChangeUserDetailsCommand.java
│ │ │ ├── dto/
│ │ │ │ └── BaseUserDto.java
│ │ │ └── query/
│ │ │ └── GetUserQuery.java
│ │ ├── domain/
│ │ │ ├── DomainRegistry.java
│ │ │ ├── EncryptionService.java
│ │ │ ├── oauth2/
│ │ │ │ ├── OAuth2Authentication.java
│ │ │ │ ├── OAuth2Principal.java
│ │ │ │ └── OAuth2Service.java
│ │ │ ├── registration/
│ │ │ │ ├── EmailAuthenticationService.java
│ │ │ │ ├── PasswordService.java
│ │ │ │ ├── UserRegistration.java
│ │ │ │ ├── UserRegistrationId.java
│ │ │ │ ├── UserRegistrationRepository.java
│ │ │ │ ├── UserRegistrationStatus.java
│ │ │ │ ├── UsersCounter.java
│ │ │ │ ├── events/
│ │ │ │ │ ├── PasswordChanged.java
│ │ │ │ │ ├── PasswordRecovered.java
│ │ │ │ │ ├── PasswordRecoveryCodeCreated.java
│ │ │ │ │ ├── UserRegistrationConfirmed.java
│ │ │ │ │ └── UserRegistrationRequested.java
│ │ │ │ └── rules/
│ │ │ │ ├── PasswordRecoveryCodeShouldBeExpiredOrNull.java
│ │ │ │ ├── PasswordRecoveryCodeShouldNotExpired.java
│ │ │ │ ├── PasswordsMustMatch.java
│ │ │ │ ├── RecoveryCodeMustMatch.java
│ │ │ │ ├── UserRegistrationCannotBeConfirmedAfterExpiration.java
│ │ │ │ ├── UserRegistrationCannotBeConfirmedMoreThanOnce.java
│ │ │ │ ├── UserRegistrationMustBeConfirmed.java
│ │ │ │ └── UserRegistrationMustBeUnique.java
│ │ │ ├── session/
│ │ │ │ ├── JwtTokenProvider.java
│ │ │ │ ├── Session.java
│ │ │ │ ├── SessionAuthenticationService.java
│ │ │ │ ├── SessionId.java
│ │ │ │ ├── SessionRepository.java
│ │ │ │ ├── TokenProvider.java
│ │ │ │ ├── events/
│ │ │ │ │ └── SessionCreated.java
│ │ │ │ └── rule/
│ │ │ │ └── SessionCannotBeExpiredWhenRefreshTokenIsMissing.java
│ │ │ └── users/
│ │ │ ├── EmailLogin.java
│ │ │ ├── User.java
│ │ │ ├── UserId.java
│ │ │ ├── UserRepository.java
│ │ │ ├── events/
│ │ │ │ ├── UserCreated.java
│ │ │ │ └── UserNameChanged.java
│ │ │ └── rules/
│ │ │ └── UserEmailMustBeUnique.java
│ │ └── infrastructure/
│ │ ├── McAuthenticationModuleExecutor.java
│ │ ├── http/
│ │ │ └── oauth2/
│ │ │ ├── AbstractOAuth2Authentication.java
│ │ │ ├── CustomOAuth2UserService.java
│ │ │ ├── FacebookOAuth2Authentication.java
│ │ │ ├── GoogleOAuth2Authentication.java
│ │ │ └── user/
│ │ │ ├── FacebookOAuth2UserInfo.java
│ │ │ ├── GoogleOAuth2UserInfo.java
│ │ │ ├── OAuth2UserInfo.java
│ │ │ └── OAuth2UserInfoFactory.java
│ │ ├── persistence/
│ │ │ ├── BaseJpaAdapter.java
│ │ │ ├── SessionJpaRepository.java
│ │ │ ├── SessionRepositoryJpaAdapter.java
│ │ │ ├── UserJpaRepository.java
│ │ │ ├── UserRegistrationJpaRepository.java
│ │ │ ├── UserRegistrationJpaRepositoryAdapter.java
│ │ │ └── UserRepositoryJpaAdapter.java
│ │ ├── processing/
│ │ │ ├── ErrorCommandHandlerDecorator.java
│ │ │ ├── LoggingCommandHandlerDecorator.java
│ │ │ ├── LoggingQueryHandlerDecorator.java
│ │ │ ├── PipelineBuilder.java
│ │ │ └── builder/
│ │ │ ├── AbstractPipelineBuilder.java
│ │ │ ├── CommandHandlerPipelineBuilder.java
│ │ │ └── QueryHandlerPipelineBuilder.java
│ │ ├── service/
│ │ │ └── MD5EncryptionService.java
│ │ ├── springboot/
│ │ │ ├── configuration/
│ │ │ │ ├── AppConfig.java
│ │ │ │ ├── AppProperties.java
│ │ │ │ ├── GUIProperties.java
│ │ │ │ ├── MessageProperties.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ ├── SwaggerConfig.java
│ │ │ │ └── SwaggerUiWebMvcConfigurer.java
│ │ │ ├── controller/
│ │ │ │ ├── AbstractController.java
│ │ │ │ ├── AuthenticationController.java
│ │ │ │ ├── RegistrationController.java
│ │ │ │ ├── RestApiRoutes.java
│ │ │ │ └── UserController.java
│ │ │ ├── filter/
│ │ │ │ └── TokenAuthenticationFilter.java
│ │ │ └── security/
│ │ │ ├── CurrentUser.java
│ │ │ ├── OAuth2AuthenticationFailureHandler.java
│ │ │ ├── OAuth2AuthenticationSuccessHandler.java
│ │ │ ├── UserAuthPrincipal.java
│ │ │ └── UserAuthToken.java
│ │ └── util/
│ │ └── CookieUtils.java
│ └── resources/
│ ├── application.yml
│ ├── db/
│ │ └── migration/
│ │ └── V2022_02_02_1124__initial_structure.sql
│ └── repo/
│ └── org/
│ └── tomo/
│ └── ddd_common/
│ ├── 1.0.0/
│ │ ├── ddd_common-1.0.0.jar
│ │ ├── ddd_common-1.0.0.jar.md5
│ │ ├── ddd_common-1.0.0.jar.sha1
│ │ ├── ddd_common-1.0.0.pom
│ │ ├── ddd_common-1.0.0.pom.md5
│ │ └── ddd_common-1.0.0.pom.sha1
│ ├── maven-metadata.xml
│ ├── maven-metadata.xml.md5
│ └── maven-metadata.xml.sha1
└── test/
├── java/
│ └── com/
│ └── tomo/
│ └── mcauthentication/
│ ├── integration/
│ │ ├── BaseIntegrationTest.java
│ │ └── application/
│ │ ├── AbstractApplicationServiceTest.java
│ │ ├── authentication/
│ │ │ ├── EmailLoginCommandHandlerTest.java
│ │ │ ├── FacebookLoginCommandHandlerTest.java
│ │ │ ├── GoogleLoginCommandHandlerTest.java
│ │ │ ├── LogoutCommandHandlerTest.java
│ │ │ └── SessionAuthenticationCommandHandlerTest.java
│ │ ├── recovery/
│ │ │ ├── CreatePasswordRecoveryCodeCommandHandlerTest.java
│ │ │ ├── GetUserRegistrationWithRecoveryCodeQueryHandlerTest.java
│ │ │ └── UpdatePasswordWithRecoveryCodeCommandHandlerTest.java
│ │ ├── registration/
│ │ │ ├── ChangePasswordCommandHandlerTest.java
│ │ │ ├── ConfirmUserRegistrationCommandHandlerTest.java
│ │ │ └── RegisterNewUserCommandHandlerTest.java
│ │ └── users/
│ │ ├── ChangeUserDetailsCommandHandlerTest.java
│ │ └── GetUserQueryHandlerTest.java
│ ├── smoke/
│ │ └── McAuthenticationApplicationSmokeTest.java
│ ├── testdata/
│ │ ├── CommandObjectMother.java
│ │ └── StaticFields.java
│ ├── unit/
│ │ ├── application/
│ │ │ └── registration/
│ │ │ └── UserRegistrationCommandHandlerTest.java
│ │ └── domain/
│ │ ├── AbstractUnitTest.java
│ │ ├── registration/
│ │ │ ├── UserRegistrationTest.java
│ │ │ └── rules/
│ │ │ ├── PasswordRecoveryCodeShouldBeExpiredOrNullTest.java
│ │ │ ├── PasswordRecoveryCodeShouldNotExpiredTest.java
│ │ │ ├── PasswordsMustMatchTest.java
│ │ │ ├── RecoveryCodeMustMatchTest.java
│ │ │ ├── UserRegistrationCannotBeConfirmedAfterExpirationTest.java
│ │ │ ├── UserRegistrationCannotBeConfirmedMoreThanOnceTest.java
│ │ │ ├── UserRegistrationMustBeConfirmedTest.java
│ │ │ └── UserRegistrationMustBeUniqueTest.java
│ │ ├── session/
│ │ │ └── rules/
│ │ │ └── SessionCannotBeExpiredWhenRefreshTokenIsMissingTest.java
│ │ └── user/
│ │ └── rules/
│ │ └── UserEmailMustBeUniqueTest.java
│ └── weblayer/
│ ├── BaseWebLayerTest.java
│ └── springboot/
│ └── controller/
│ ├── AbstractControllerTest.java
│ └── RegistrationControllerTest.java
└── resources/
└── application-ci.yml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/maven.yml
================================================
name: Run tests
on:
pull_request:
branches:
- main
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
env:
RAILS_ENV: test
steps:
- name: Checkout code
uses: actions/checkout@v3
- uses: satackey/action-docker-layer-caching@v0.0.11
continue-on-error: true
- name: Start docker containers
run: |
docker-compose -f docker-compose-ci.yml up --build --detach
sleep 10 # wait for database to be ready
- name: Run unit tests
run: docker-compose -f docker-compose-ci.yml run mc-authentication-service-test ./mvnw test -Punittest -Dspring.profiles.active=ci
- name: Run integration tests
run: docker-compose -f docker-compose-ci.yml run mc-authentication-service-test ./mvnw test -Pintegrationtest -Dspring.profiles.active=ci
================================================
FILE: .gitignore
================================================
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/
.DS_Store
================================================
FILE: .mvn/wrapper/MavenWrapperDownloader.java
================================================
/*
* Copyright 2007-present the original author or authors.
*
* Licensed 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
*
* https://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.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}
================================================
FILE: .mvn/wrapper/maven-wrapper.properties
================================================
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
================================================
FILE: 1-init.sql
================================================
create user mcuser with superuser;
alter user mcuser with password 'mcuser';
create database mc_authentication with owner mcuser;
create extension "uuid-ossp";
================================================
FILE: Dockerfile
================================================
FROM amazoncorretto:11 as base
WORKDIR /app
COPY .mvn/ .mvn
COPY mvnw pom.xml ddd_common-v1.0.0.jar ./
RUN ./mvnw deploy:deploy-file -Durl=file:./src/main/resources/repo -Dfile=ddd_common-v1.0.0.jar -DgroupId=org.tomo -DartifactId=ddd_common -Dpackaging=jar -Dversion=1.0.0
RUN ./mvnw dependency:resolve
FROM base as test
RUN ./mvnw dependency:resolve-plugins -Punittest,integrationtest
COPY src ./src
ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" skipcache
CMD tail -f /dev/null
FROM base as build
COPY src ./src
RUN ./mvnw package
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Tomislav Landeka
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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 NONINFRINGEMENT. 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.
================================================
FILE: README.md
================================================
# Microservice for authentication with Domain-Driven Design
Full Spring Boot authentication microservice with Domain-Driven Design approach.
### 1. Introduction
#### 1.1. Purpose of this Repository
This is a list of the main goals of this repository:
* Showing how you can implement a Domain-Drive design
* Showing how you can implement a CQRS
* Presentation of the full implementation of an application
* This is not another proof of concept (PoC)
* The goal is to present the implementation of an application that would be ready to run in production
* Showing the application of best practices and object-oriented programming principles
* Presentation of the use of design patterns. When, how and why they can be used
* Presentation of the implementation using Domain-Driven Design approach (tactical patterns)
* Presentation of the implementation of Unit Tests for Domain Model (Testable Design in mind)
* Presentation of the implementation of Integration Tests
* Presentation of the implementation of testing Web-Layer only
#### 1.2 Your contribution
* Give it a star
* Share it
* Become a contributor
### 2. Domain
#### 2.1. Business logic of the repository
The main idea of this repository is to create small microservice for authentication that provides next functionalities:
* Form Registration
* Form Login, Google Login, Facebook Login
* Password recovery
* Email notifications
* Session identification and authentication
#### 2.2. How to run
* Run database docker container first: `docker-compose up -d`
* Run application locally with `local` profile
* Plan is to dockerize the application in order to run it easy
#### 2.3. How to run tests
* Run database docker container first: `docker-compose up -d`. Plan is to add testdb for testing purposes.
* Run Integration test separately
* Run Unit test separately
* Run Web-Layer (tests that are testing Spring Boot implementation and Rest Controller) tests separately
### 3. Desired TODO List to complete the application:
* Clean the code from inconsistencies
* Add more session details
* Implement a tool like ELK
* Caching if/when needed
* More tests
* CI
* Pull out 'ddd' folder and create a library
* Pull out 'CQRS' folders (contracts and configuration) and create a library
* Finish SpringBoot security and CSFR token
* Dockerize
### 4. The main idea is to create a reusable microservice network that consists of the next microservices:
* Service discovery (probably with K8s).
* Create a microservice for authentication (current repository).
* Create a microservice for authorization - simple implementation of RBAC.
* Create a microservice for sending emails.
* Create a microservice for localization - the idea is to provide UI for translating the application to various languages as a common part of most applications.
* Create a microservice for asynchronous communication (AC) - the idea is to create a microservice that distributes the messages between microservices. The microservice should work over DB (e.g. Redis) and RMQ to provide asynchrony. The microservice should provide the REST API for accessing it. In this way, we should have RMQ in only one place and communication with this microservice should go over REST API.
The microservice should provide these routes:
* route for registering messages by other microservices. E.g. Email-Microservice could register message **send-email** with _required properties_, _endpoint_, and _version_. That configuration should be saved into a DB.
* sending messages - E.g. Authentication microservice should send a message after registering a user with the name **send-email** and _required properties_. The AC microservice will receive that message, validate the required properties, enrich the message body with an endpoint (which is saved in DB) and publish a message to RMQ. The RMQ consumer will consume the message and distribute it to the endpoint.
### 5. License
The project is under [MIT license](https://opensource.org/licenses/MIT).
### 6. Inspiration
#### 6.1. Domain-Driven Design
- ["Domain-Driven Design: Tackling Complexity in the Heart of Software"](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215) book, Eric Evans
- ["Implementing Domain-Driven Design"](https://www.amazon.com/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577) book, Vaughn Vernon
- ["Domain-Driven Design in PHP"](https://www.amazon.com/dp/1787284948) book, by Carlos Buenosvinos, Christian Soronellas, Keyvan Akbary
- ["IDDD_Samples"](https://github.com/VaughnVernon/IDDD_Samples) GH repository, Vaughn Vernon
- ["Blog CQRS"](https://github.com/dddshelf/blog-cqrs) GH repository, by Carlos Buenosvinos, Christian Soronellas, Keyvan Akbary
- ["DDD example - Last Wishes"](https://github.com/dddshelf/last-wishes) GH repository, by Carlos Buenosvinos, Christian Soronellas, Keyvan Akbary
- ["Modular Monolith with DDD"](https://github.com/kgrzybek/modular-monolith-with-ddd) GH repository, by Kamil Grzybek
- ["Mediator implementation"](https://github.com/jbogard/MediatR) GH repository, by Jimmy Bogard
#### 6.2 Application Architecture
- ["Patterns of Enterprise Application Architecture"](https://martinfowler.com/books/eaa.html) book, Martin Fowler
- ["Clean Architecture: A Craftsman's Guide to Software Structure and Design (Robert C. Martin Series"](https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164) book, Robert C. Martin
- ["The Clean Architecture"](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) article, Robert C. Martin
- ["DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together"](https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/) article, Herberto Graca
#### 6.2. System Architecture
- ["Building Microservices: Designing Fine-Grained Systems"](https://www.amazon.com/Building-Microservices-Designing-Fine-Grained-Systems/dp/1491950358) book, Sam Newman
#### 6.3. Design
- ["Clean Code: A Handbook of Agile Software Craftsmanship"](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) book, Robert C. Martin
- ["Design Patterns: Elements of Reusable Object-Oriented Software"](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) book, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
================================================
FILE: docker-compose-ci.yml
================================================
version: '3.7'
services:
mc-authentication-db-test:
container_name: mc-authentication-db-test
image: postgres:12
restart: always
ports:
- 127.0.0.1:5432:5432
networks:
- mc-network-test
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
volumes:
- mc-authentication-db-test:/var/lib/postgresql
- ./1-init.sql:/docker-entrypoint-initdb.d/1-init.sql
mc-authentication-service-test:
container_name: mc-authentication-service-test
depends_on:
- mc-authentication-db-test
build:
context: ./
dockerfile: Dockerfile
target: test
ports:
- 127.0.0.1:8080:8080
networks:
- mc-network-test
volumes:
mc-authentication-db-test:
networks:
mc-network-test:
driver: bridge
================================================
FILE: docker-compose.yml
================================================
version: '3.7'
services:
mc-authentication_db:
container_name: mc-authentication_db
image: postgres:12
restart: always
ports:
- 127.0.0.1:5432:5432
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
volumes:
- mc-authentication-db:/var/lib/postgresql
- ./1-init.sql:/docker-entrypoint-initdb.d/1-init.sql
volumes:
mc-authentication-db:
networks:
mc-network:
driver: bridge
================================================
FILE: mvnw
================================================
#!/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
#
# https://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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
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"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
================================================
FILE: mvnw.cmd
================================================
@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 https://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 Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%
================================================
FILE: pom.xml
================================================
4.0.0
com.tomo
mc-authentication
0.0.1-SNAPSHOT
mc-authentication
API for Authentication
org.springframework.boot
spring-boot-starter-parent
2.6.2
UTF-8
UTF-8
11
4.13.2
4.12.1
5.8.2
1.0.1
project.local
project
file:${project.basedir}/src/main/resources/repo
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.security
spring-security-test
test
org.springframework.boot
spring-boot-starter-security
org.springframework.security
spring-security-oauth2-client
io.jsonwebtoken
jjwt-api
0.11.2
io.jsonwebtoken
jjwt-impl
0.11.2
runtime
io.jsonwebtoken
jjwt-jackson
0.11.2
runtime
org.projectlombok
lombok
1.18.22
provided
net.sizovs
pipelinr
0.7
org.postgresql
postgresql
runtime
org.flywaydb
flyway-core
8.4.3
org.codehaus.mojo
exec-maven-plugin
1.2.1
org.apache.commons
commons-lang3
3.12.0
javax.validation
validation-api
2.0.1.Final
org.modelmapper
modelmapper
2.4.5
com.github.javafaker
javafaker
1.0.2
net.sargue
mailgun
1.10.0
org.glassfish.jersey.inject
jersey-hk2
2.28
com.google.code.gson
gson
2.8.5
io.springfox
springfox-boot-starter
3.0.0
org.mockito
mockito-inline
3.8.0
test
org.tomo
ddd_common
v1.0.0
org.tomo
ddd_common
1.0.0
flyway
org.flywaydb
flyway-maven-plugin
8.4.3
generate-sources
migrate
org.postgresql.Driver
mcuser
mcuser
jdbc:postgresql://localhost:5432/mc_authentication
false
true
public
unittest
maven-surefire-plugin
2.19.1
com.tomo.mcauthentication.unit.**
org.junit.platform
junit-platform-surefire-provider
${junit-platform.version}
org.junit.vintage
junit-vintage-engine
${junit-vintage-engine}
org.junit.jupiter
junit-jupiter-engine
${junit-jupiter.version}
integrationtest
org.springframework.boot
spring-boot-maven-plugin
org.flywaydb
flyway-maven-plugin
8.4.3
generate-sources
migrate
org.postgresql.Driver
mcuser
mcuser
jdbc:postgresql://mc-authentication-db-test:5432/mc_authentication
false
true
public
org.apache.maven.plugins
maven-failsafe-plugin
com.tomo.mcauthentication.integration.*
integration-test
integration-test
verify
maven-surefire-plugin
2.19.1
com.tomo.mcauthentication.unit.**
org.junit.platform
junit-platform-surefire-provider
${junit-platform.version}
org.junit.vintage
junit-vintage-engine
${junit-vintage-engine}
org.junit.jupiter
junit-jupiter-engine
${junit-jupiter.version}
================================================
FILE: src/main/java/com/tomo/mcauthentication/McAuthenticationApplication.java
================================================
package com.tomo.mcauthentication;
import com.tomo.mcauthentication.infrastructure.springboot.configuration.AppProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@EnableWebMvc
@EnableSwagger2
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
@EnableAspectJAutoProxy(proxyTargetClass=true)
@ComponentScan(basePackages = { "com.tomo.*" })
@EntityScan("com.tomo.*")
public class McAuthenticationApplication {
public static void main(String[] args) {
SpringApplication.run(McAuthenticationApplication.class, args);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/BaseMapper.java
================================================
package com.tomo.mcauthentication.application;
import org.modelmapper.ModelMapper;
public class BaseMapper {
protected ModelMapper modelMapper;
public BaseMapper() {}
public BaseMapper(ModelMapper modelMapper) {
this.modelMapper = modelMapper;
}
protected T toDto(E source, Class destinationType) {
return modelMapper.map(source, destinationType);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/McAuthenticationEventHandler.java
================================================
package com.tomo.mcauthentication.application;
import com.tomo.ddd.domain.DomainEvent;
import com.tomo.ddd.domain.DomainEventPublisher;
import com.tomo.ddd.domain.DomainEventSubscriber;
import com.tomo.ddd.event.EventStore;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class McAuthenticationEventHandler {
private EventStore eventStore;
public McAuthenticationEventHandler(EventStore eventStore) {
this.eventStore = eventStore;
}
@Before(value = "execution(* *(..)) && within(com.tomo.mcauthentication.application..*)")
public void listen() {
DomainEventPublisher
.instance()
.subscribe(new DomainEventSubscriber() {
public void handleEvent(DomainEvent aDomainEvent) {
store(aDomainEvent);
}
public Class subscribedToEventType() {
return DomainEvent.class; // all domain events
}
});
}
/**
* Stores aDomainEvent to the event store.
* @param aDomainEvent the DomainEvent to store
*/
private void store(DomainEvent aDomainEvent) {
this.eventStore().append(aDomainEvent);
}
/**
* Answers my EventStore.
* @return EventStore
*/
private EventStore eventStore() {
return this.eventStore;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/BaseLoginCommandHandler.java
================================================
package com.tomo.mcauthentication.application.authentication;
import com.tomo.mcauthentication.application.BaseMapper;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionRepository;
import com.tomo.mcauthentication.domain.session.TokenProvider;
import org.modelmapper.ModelMapper;
public class BaseLoginCommandHandler extends BaseMapper {
protected SessionRepository sessionRepository;
protected TokenProvider tokenProvider;
public BaseLoginCommandHandler() {
}
public BaseLoginCommandHandler(ModelMapper modelMapper, TokenProvider tokenProvider) {
super(modelMapper);
this.tokenProvider = tokenProvider;
}
public BaseLoginCommandHandler(
ModelMapper modelMapper,
SessionRepository sessionRepository,
TokenProvider tokenProvider) {
super(modelMapper);
this.sessionRepository = sessionRepository;
this.tokenProvider = tokenProvider;
}
protected SessionDto toDto(Session session) {
return SessionDto.create(session, this.modelMapper);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/EmailLoginCommandHandler.java
================================================
package com.tomo.mcauthentication.application.authentication;
import com.tomo.mcauthentication.application.authentication.command.EmailLoginCommand;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionRepository;
import com.tomo.mcauthentication.domain.session.TokenProvider;
import com.tomo.mcauthentication.domain.registration.EmailAuthenticationService;
import com.tomo.mcauthentication.domain.users.User;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class EmailLoginCommandHandler extends BaseLoginCommandHandler implements CommandHandler {
EmailAuthenticationService authenticationService;
public EmailLoginCommandHandler(
EmailAuthenticationService emailAuthenticationService,
@Qualifier("sessionRepositoryJpaAdapter") SessionRepository sessionRepository,
@Qualifier("jwtTokenProvider") TokenProvider tokenProvider,
ModelMapper modelMapper) {
super(modelMapper, sessionRepository, tokenProvider);
this.authenticationService = emailAuthenticationService;
this.tokenProvider = tokenProvider;
}
@Override
public SessionDto handle(EmailLoginCommand command) {
User user = authenticationService.authenticate(command.getEmail(), command.getPassword());
Session session = new Session(
sessionRepository.nextIdentity(),
user,tokenProvider,
command.getRememberMe(),
command.getUserAgent(), command.getIpAddress());
sessionRepository.save(session);
return toDto(session);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/FacebookLoginCommandHandler.java
================================================
package com.tomo.mcauthentication.application.authentication;
import com.tomo.mcauthentication.application.authentication.command.FacebookLoginCommand;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.application.users.dto.BaseUserDto;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Service;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionRepository;
import com.tomo.mcauthentication.domain.session.TokenProvider;
import com.tomo.mcauthentication.domain.users.User;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class FacebookLoginCommandHandler extends BaseLoginCommandHandler implements CommandHandler {
OAuth2Service oAuth2Service;
public FacebookLoginCommandHandler(
@Qualifier("facebookOAuth2Service") OAuth2Service oAuth2Service,
@Qualifier("jwtTokenProvider") TokenProvider tokenProvider,
@Qualifier("sessionRepositoryJpaAdapter") SessionRepository sessionRepository,
ModelMapper modelMapper) {
super(modelMapper, sessionRepository, tokenProvider);
this.oAuth2Service = oAuth2Service;
this.tokenProvider = tokenProvider;
}
@Override
public BaseUserDto handle(FacebookLoginCommand command) {
User user = oAuth2Service.registerAuthenticate(command.getAccessCode());
Session session = new Session(
sessionRepository.nextIdentity(),
user,tokenProvider,
command.getRememberMe(),
command.getUserAgent(), command.getIpAddress());
sessionRepository.save(session);
return null;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/GoogleLoginCommandHandler.java
================================================
package com.tomo.mcauthentication.application.authentication;
import com.tomo.mcauthentication.application.authentication.command.GoogleLoginCommand;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.application.users.dto.BaseUserDto;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Service;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionRepository;
import com.tomo.mcauthentication.domain.session.TokenProvider;
import com.tomo.mcauthentication.domain.users.User;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class GoogleLoginCommandHandler extends BaseLoginCommandHandler implements CommandHandler {
OAuth2Service oAuth2Service;
public GoogleLoginCommandHandler(
@Qualifier("googleOAuth2Service") OAuth2Service oAuth2Service,
@Qualifier("jwtTokenProvider") TokenProvider tokenProvider,
@Qualifier("sessionRepositoryJpaAdapter") SessionRepository sessionRepository,
ModelMapper modelMapper) {
super(modelMapper, sessionRepository, tokenProvider);
this.oAuth2Service = oAuth2Service;
this.tokenProvider = tokenProvider;
}
@Override
public BaseUserDto handle(GoogleLoginCommand command) {
User user = oAuth2Service.registerAuthenticate(command.getAccessCode());
Session session = new Session(
sessionRepository.nextIdentity(),
user,tokenProvider,
command.getRememberMe(),
command.getUserAgent(), command.getIpAddress());
sessionRepository.save(session);
return null;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/LogoutCommandHandler.java
================================================
package com.tomo.mcauthentication.application.authentication;
import com.tomo.mcauthentication.application.authentication.command.LogoutCommand;
import com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler;
import com.tomo.mcauthentication.domain.session.SessionAuthenticationService;
import org.springframework.stereotype.Service;
@Service
public class LogoutCommandHandler extends AbstractVoidyCommandHandler {
SessionAuthenticationService sessionAuthenticationService;
public LogoutCommandHandler(SessionAuthenticationService sessionAuthenticationService) {
this.sessionAuthenticationService = sessionAuthenticationService;
}
@Override
protected void abstractHandle(LogoutCommand aCommand) {
sessionAuthenticationService.logout(aCommand.getAuthToken());
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/SessionAuthenticationCommandHandler.java
================================================
package com.tomo.mcauthentication.application.authentication;
import com.tomo.mcauthentication.application.BaseMapper;
import com.tomo.mcauthentication.application.authentication.command.SessionAuthenticationCommand;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionAuthenticationService;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
@Service
public class SessionAuthenticationCommandHandler extends BaseMapper implements CommandHandler {
SessionAuthenticationService sessionAuthenticationService;
public SessionAuthenticationCommandHandler(
SessionAuthenticationService sessionAuthenticationService,
ModelMapper modelMapper) {
super(modelMapper);
this.sessionAuthenticationService = sessionAuthenticationService;
}
@Override
public SessionDto handle(SessionAuthenticationCommand command) {
Session session = sessionAuthenticationService.authenticate(command.getAuthToken());
return SessionDto.create(session, this.modelMapper);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/command/BaseLoginCommand.java
================================================
package com.tomo.mcauthentication.application.authentication.command;
import com.tomo.mcauthentication.application.contracts.BaseCommand;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class BaseLoginCommand extends BaseCommand {
private Boolean rememberMe;
private String userAgent;
private String ipAddress;
public BaseLoginCommand() {
super();
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/command/EmailLoginCommand.java
================================================
package com.tomo.mcauthentication.application.authentication.command;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class EmailLoginCommand extends BaseLoginCommand {
@NotBlank
@Email
private String email;
@NotBlank
private String password;
private Boolean rememberMe;
private String userAgent;
private String ipAddress;
public EmailLoginCommand() {
super();
}
public EmailLoginCommand(String email, String password) {
super();
this.email = email;
this.password = password;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/command/FacebookLoginCommand.java
================================================
package com.tomo.mcauthentication.application.authentication.command;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
public class FacebookLoginCommand extends BaseLoginCommand {
private String accessCode;
public FacebookLoginCommand(String accessCode) {
this.accessCode = accessCode;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/command/GoogleLoginCommand.java
================================================
package com.tomo.mcauthentication.application.authentication.command;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
public class GoogleLoginCommand extends BaseLoginCommand {
private String accessCode;
public GoogleLoginCommand(String accessCode) {
this.accessCode = accessCode;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/command/LogoutCommand.java
================================================
package com.tomo.mcauthentication.application.authentication.command;
import com.tomo.mcauthentication.application.contracts.security.AbstractAuthenticateCommand;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
public class LogoutCommand extends AbstractAuthenticateCommand {
public LogoutCommand(String authToken) {
super(authToken);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/command/SessionAuthenticationCommand.java
================================================
package com.tomo.mcauthentication.application.authentication.command;
import com.tomo.mcauthentication.application.contracts.security.AbstractAuthenticateCommand;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class SessionAuthenticationCommand extends AbstractAuthenticateCommand {
public SessionAuthenticationCommand(String authToken) {
super(authToken);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/dto/RecoveryPasswordDto.java
================================================
package com.tomo.mcauthentication.application.authentication.dto;
import com.tomo.mcauthentication.application.contracts.Response;
import lombok.Getter;
@Getter
public class RecoveryPasswordDto implements Response {
String recoveryCode;
public RecoveryPasswordDto(String recoveryCode) {
this.recoveryCode = recoveryCode;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/authentication/dto/SessionDto.java
================================================
package com.tomo.mcauthentication.application.authentication.dto;
import com.tomo.mcauthentication.application.users.dto.BaseUserDto;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionId;
import com.tomo.mcauthentication.domain.users.UserId;
import org.modelmapper.ModelMapper;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class SessionDto extends BaseUserDto {
private String sessionId;
private String accessToken;
private LocalDateTime expirationDate;
int expirationDateMilis;
private String tokenType;
private String refreshToken;
private String userAgent;
private String ipAddress;
private LocalDateTime lastActivity;
private String userId;
public static SessionDto create(Session session, ModelMapper mapper) {
SessionDto dto = mapper.map(session, SessionDto.class);
dto.setTokenType(session.getTokenType());
dto.setUserId(session.getUserId());
return dto;
}
public void setSessionId(SessionId sessionId) {
this.sessionId = sessionId.toString();
}
public void setTokenType(Session.TokenType tokenType) {
this.tokenType = tokenType.toString();
}
public void setUserId(UserId userId) {
this.userId = userId.id().toString();
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/configuration/AbstractVoidyCommandHandler.java
================================================
package com.tomo.mcauthentication.application.configuration;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.Voidy;
import javax.transaction.Transactional;
public abstract class AbstractVoidyCommandHandler implements CommandHandler {
@Override
@Transactional
public Voidy handle(T aCommand) {
abstractHandle(aCommand);
return new Voidy();
}
abstract protected void abstractHandle(T aCommand);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/configuration/CommandHandler.java
================================================
package com.tomo.mcauthentication.application.configuration;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.Response;
public interface CommandHandler extends RequestHandler {
R handle(T aCommand);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/configuration/QueryHandler.java
================================================
package com.tomo.mcauthentication.application.configuration;
import com.tomo.mcauthentication.application.contracts.Query;
import com.tomo.mcauthentication.application.contracts.Response;
public interface QueryHandler extends RequestHandler {
R handle(T query);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/configuration/RequestHandler.java
================================================
package com.tomo.mcauthentication.application.configuration;
import com.tomo.mcauthentication.application.contracts.Request;
import com.tomo.mcauthentication.application.contracts.Response;
public interface RequestHandler {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/BaseCommand.java
================================================
package com.tomo.mcauthentication.application.contracts;
public class BaseCommand extends BaseRequest implements Command {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/BaseQuery.java
================================================
package com.tomo.mcauthentication.application.contracts;
public class BaseQuery extends BaseRequest implements Query {}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/BaseRequest.java
================================================
package com.tomo.mcauthentication.application.contracts;
import java.util.UUID;
public class BaseRequest implements Request {
private UUID id;
public BaseRequest() {
setId(UUID.randomUUID());
}
public BaseRequest(UUID anId) {
this.id = anId;
}
@Override
public UUID id() {
return this.id;
}
protected void setId(UUID anId) {
this.id = anId;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/Command.java
================================================
package com.tomo.mcauthentication.application.contracts;
public interface Command extends Request {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/Identifiable.java
================================================
package com.tomo.mcauthentication.application.contracts;
import java.util.UUID;
public interface Identifiable {
UUID id();
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/McAuthenticationModule.java
================================================
package com.tomo.mcauthentication.application.contracts;
import org.springframework.stereotype.Component;
@Component
public interface McAuthenticationModule {
Response executeCommand(Command command);
Response executeQuery(Query query);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/Query.java
================================================
package com.tomo.mcauthentication.application.contracts;
public interface Query extends Request {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/Request.java
================================================
package com.tomo.mcauthentication.application.contracts;
public interface Request extends Identifiable {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/Response.java
================================================
package com.tomo.mcauthentication.application.contracts;
public interface Response {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/Voidy.java
================================================
package com.tomo.mcauthentication.application.contracts;
public class Voidy implements Response {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/security/AbstractAuthenticateCommand.java
================================================
package com.tomo.mcauthentication.application.contracts.security;
import com.tomo.mcauthentication.application.contracts.Command;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public abstract class AbstractAuthenticateCommand extends AbstractAuthenticateRequest implements Command {
public AbstractAuthenticateCommand(String authToken) {
super(authToken);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/security/AbstractAuthenticateQuery.java
================================================
package com.tomo.mcauthentication.application.contracts.security;
import com.tomo.mcauthentication.application.contracts.BaseRequest;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.Query;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public abstract class AbstractAuthenticateQuery extends AbstractAuthenticateRequest implements Query {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/security/AbstractAuthenticateRequest.java
================================================
package com.tomo.mcauthentication.application.contracts.security;
import com.tomo.mcauthentication.application.contracts.BaseRequest;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.Request;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public abstract class AbstractAuthenticateRequest extends BaseRequest implements Authenticate, Request {
String authToken;
public AbstractAuthenticateRequest(String authToken) {
this.authToken = authToken;
}
@Override
public String authToken() {
return this.authToken;
}
@Override
public void setAuthToken(String authToken) {
this.authToken = authToken;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/security/AbstractAuthorizeCommand.java
================================================
package com.tomo.mcauthentication.application.contracts.security;
import com.tomo.mcauthentication.application.contracts.Command;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public abstract class AbstractAuthorizeCommand extends AbstractAuthorizeRequest implements Command {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/security/AbstractAuthorizeQuery.java
================================================
package com.tomo.mcauthentication.application.contracts.security;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.Query;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public abstract class AbstractAuthorizeQuery extends AbstractAuthorizeRequest implements Query {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/security/AbstractAuthorizeRequest.java
================================================
package com.tomo.mcauthentication.application.contracts.security;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.Request;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public abstract class AbstractAuthorizeRequest extends AbstractAuthenticateRequest implements Request, Authorize {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/security/Authenticate.java
================================================
package com.tomo.mcauthentication.application.contracts.security;
public interface Authenticate {
String authToken();
void setAuthToken(String authToken);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/contracts/security/Authorize.java
================================================
package com.tomo.mcauthentication.application.contracts.security;
import java.util.List;
public interface Authorize extends Authenticate {
default List getAuthorities() {
return List.of("USER");
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/CreatePasswordRecoveryCodeCommandHandler.java
================================================
package com.tomo.mcauthentication.application.recovery;
import com.tomo.mcauthentication.application.recovery.command.CreatePasswordRecoveryCodeCommand;
import com.tomo.mcauthentication.application.authentication.dto.RecoveryPasswordDto;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.domain.registration.EmailAuthenticationService;
import org.springframework.stereotype.Service;
@Service
public class CreatePasswordRecoveryCodeCommandHandler implements CommandHandler {
EmailAuthenticationService emailAuthenticationService;
public CreatePasswordRecoveryCodeCommandHandler(EmailAuthenticationService emailAuthenticationService) {
this.emailAuthenticationService = emailAuthenticationService;
}
@Override
public RecoveryPasswordDto handle(CreatePasswordRecoveryCodeCommand aCommand) {
return new RecoveryPasswordDto(
emailAuthenticationService
.createPasswordRecoveryCode(aCommand.getEmail())
);
//todo send recovery code
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/GetUserRegistrationWithRecoveryCodeQueryHandler.java
================================================
package com.tomo.mcauthentication.application.recovery;
import com.tomo.mcauthentication.application.BaseMapper;
import com.tomo.mcauthentication.application.configuration.QueryHandler;
import com.tomo.mcauthentication.application.registration.dto.UserRegistrationDto;
import com.tomo.mcauthentication.application.recovery.dto.GetUserRegistrationWithRecoveryCodeQuery;
import com.tomo.mcauthentication.domain.EncryptionService;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Component;
@Component
public class GetUserRegistrationWithRecoveryCodeQueryHandler extends BaseMapper implements QueryHandler {
private UserRegistrationRepository userRegistrationRepository;
private EncryptionService encryptionService;
public GetUserRegistrationWithRecoveryCodeQueryHandler(
UserRegistrationRepository aUserRegistrationRepository,
EncryptionService anEncryptionService,
ModelMapper modelMapper) {
super(modelMapper);
this.userRegistrationRepository = aUserRegistrationRepository;
this.encryptionService = anEncryptionService;
}
@Override
public UserRegistrationDto handle(GetUserRegistrationWithRecoveryCodeQuery query) {
UserRegistration userRegistration = userRegistrationRepository
.findByRecoveryCode(encryptionService.encryptedValue(query.getRecoveryCode()));
if (userRegistration == null) {
throw new IllegalStateException(String.format("User with recovery code %s doesn't exists.", query.getRecoveryCode()));
}
UserRegistrationDto dto = toDto(
userRegistration,
UserRegistrationDto.class
);
return dto;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/PasswordRecoveryCodeCreatedEventHandler.java
================================================
package com.tomo.mcauthentication.application.recovery;
import com.tomo.mcauthentication.application.contracts.McAuthenticationModule;
import com.tomo.mcauthentication.application.recovery.command.SendPasswordRecoveryEmailCommand;
import com.tomo.ddd.domain.DomainEventPublisher;
import com.tomo.ddd.domain.DomainEventSubscriber;
import com.tomo.mcauthentication.domain.registration.events.PasswordRecoveryCodeCreated;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class PasswordRecoveryCodeCreatedEventHandler {
private McAuthenticationModule module;
/**
* In order to not mix business logic with plugins like a GUI..
* baseUrl + URI + queryString eg. localhost/reset-passwrod/?recoveryCode=
*/
private String recoveryLink;
public PasswordRecoveryCodeCreatedEventHandler(McAuthenticationModule authenticationModule, String recoveryLink) {
this.module = authenticationModule;
this.recoveryLink = recoveryLink;
}
@Before("execution(" +
"public * com.tomo.mcauthentication.application.configuration.CommandHandler.*(..)) && " +
"target(com.tomo.mcauthentication.application.recovery.CreatePasswordRecoveryCodeCommandHandler))")
public void listen() {
DomainEventPublisher
.instance()
.subscribe(new DomainEventSubscriber() {
public void handleEvent(PasswordRecoveryCodeCreated aDomainEvent) {
module.executeCommand(new SendPasswordRecoveryEmailCommand(
aDomainEvent.getEmail(),
aDomainEvent.getRecoveryCode(),
recoveryLink,
aDomainEvent.getRecoveryCodeExpirationDate()));
}
public Class subscribedToEventType() {
return PasswordRecoveryCodeCreated.class;
}
});
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/SendPasswordRecoveryEmailCommandHandler.java
================================================
package com.tomo.mcauthentication.application.recovery;
import com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler;
import com.tomo.mcauthentication.application.recovery.command.SendPasswordRecoveryEmailCommand;
import com.tomo.ddd.email.EmailSender;
import com.tomo.ddd.port.adapter.message.email.EmailMessage;
import org.springframework.stereotype.Component;
@Component
public class SendPasswordRecoveryEmailCommandHandler extends AbstractVoidyCommandHandler {
EmailSender emailMessageSender;
public SendPasswordRecoveryEmailCommandHandler(EmailSender emailMessageSender) {
this.emailMessageSender = emailMessageSender;
}
@Override
protected void abstractHandle(SendPasswordRecoveryEmailCommand aCommand) {
//todo maybe validate if exist
this.emailMessageSender.send(new EmailMessage(
aCommand.getEmail(), ""
, " Recovery code",
"To reset your password, visit the following link: " + aCommand.getRecoveryLink() + aCommand.getRecoveryCode()));
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/UpdatePasswordWithRecoveryCodeCommandHandler.java
================================================
package com.tomo.mcauthentication.application.recovery;
import com.tomo.mcauthentication.application.recovery.command.UpdatePasswordWithRecoveryCodeCommand;
import com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler;
import com.tomo.mcauthentication.domain.registration.EmailAuthenticationService;
import org.springframework.stereotype.Service;
@Service
public class UpdatePasswordWithRecoveryCodeCommandHandler extends AbstractVoidyCommandHandler {
EmailAuthenticationService emailAuthenticationService;
public UpdatePasswordWithRecoveryCodeCommandHandler(EmailAuthenticationService emailAuthenticationService) {
this.emailAuthenticationService = emailAuthenticationService;
}
@Override
protected void abstractHandle(UpdatePasswordWithRecoveryCodeCommand aCommand) {
emailAuthenticationService.recoverPasswordWithRecoveryCode(
aCommand.getRecoveryCode(),
aCommand.getNewPassword(),
aCommand.getNewPasswordRepeated());
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/command/CreatePasswordRecoveryCodeCommand.java
================================================
package com.tomo.mcauthentication.application.recovery.command;
import com.tomo.mcauthentication.application.contracts.BaseCommand;
import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class CreatePasswordRecoveryCodeCommand extends BaseCommand {
@NotNull
private String email;
public CreatePasswordRecoveryCodeCommand(String email) {
this.email = email;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/command/SendPasswordRecoveryEmailCommand.java
================================================
package com.tomo.mcauthentication.application.recovery.command;
import com.tomo.mcauthentication.application.contracts.BaseCommand;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class SendPasswordRecoveryEmailCommand extends BaseCommand {
String email;
String recoveryCode;
String recoveryLink;
LocalDateTime recoveryCodeExpirationDate;
public SendPasswordRecoveryEmailCommand(String email, String recoveryCode, String recoveryLink, LocalDateTime recoveryCodeExpirationDate) {
this.email = email;
this.recoveryCode = recoveryCode;
this.recoveryLink = recoveryLink;
this.recoveryCodeExpirationDate = recoveryCodeExpirationDate;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/command/UpdatePasswordWithRecoveryCodeCommand.java
================================================
package com.tomo.mcauthentication.application.recovery.command;
import com.tomo.mcauthentication.application.contracts.BaseCommand;
import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class UpdatePasswordWithRecoveryCodeCommand extends BaseCommand {
@NotNull
private String newPassword;
@NotNull
private String newPasswordRepeated;
@NotNull
private String recoveryCode;
public UpdatePasswordWithRecoveryCodeCommand(String newPassword, String newPasswordRepeated, String recoveryCode) {
this.newPassword = newPassword;
this.newPasswordRepeated = newPasswordRepeated;
this.recoveryCode = recoveryCode;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/recovery/dto/GetUserRegistrationWithRecoveryCodeQuery.java
================================================
package com.tomo.mcauthentication.application.recovery.dto;
import com.tomo.mcauthentication.application.contracts.BaseQuery;
import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class GetUserRegistrationWithRecoveryCodeQuery extends BaseQuery {
@NotNull
String recoveryCode;
public GetUserRegistrationWithRecoveryCodeQuery(String recoveryCode) {
this.recoveryCode = recoveryCode;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/ChangePasswordCommandHandler.java
================================================
package com.tomo.mcauthentication.application.registration;
import com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler;
import com.tomo.mcauthentication.application.registration.command.ChangePasswordCommand;
import com.tomo.mcauthentication.domain.registration.EmailAuthenticationService;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionAuthenticationService;
import org.springframework.stereotype.Service;
@Service
public class ChangePasswordCommandHandler extends AbstractVoidyCommandHandler {
EmailAuthenticationService emailAuthenticationService;
SessionAuthenticationService sessionAuthenticationService;
UserRegistrationRepository userRegistrationRepository;
public ChangePasswordCommandHandler(
EmailAuthenticationService emailAuthenticationService,
SessionAuthenticationService aSessionAuthenticationService,
UserRegistrationRepository anUserRegistrationRepository) {
this.emailAuthenticationService = emailAuthenticationService;
this.sessionAuthenticationService = aSessionAuthenticationService;
this.userRegistrationRepository = anUserRegistrationRepository;
}
@Override
protected void abstractHandle(ChangePasswordCommand aCommand) {
Session session = sessionAuthenticationService.authenticate(aCommand.getAccessToken());
UserRegistration userRegistration = userRegistrationRepository.findByUserId(session.getUserId());
if (userRegistration == null) {
throw new IllegalStateException(String.format("User with id %s doesn't exist.", session.getUserId().id()));
}
userRegistration.changePassword(aCommand.getOldPassword(), aCommand.getNewPassword(), aCommand.getNewPasswordRepeated());
userRegistrationRepository.save(userRegistration);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/ConfirmUserRegistrationCommandHandler.java
================================================
package com.tomo.mcauthentication.application.registration;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.application.registration.command.ConfirmUserRegistrationCommand;
import com.tomo.mcauthentication.application.users.dto.BaseUserDto;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserRepository;
import org.springframework.stereotype.Component;
@Component
public class ConfirmUserRegistrationCommandHandler implements CommandHandler {
UserRegistrationRepository userRegistrationRepository;
UserRepository userRepository;
public ConfirmUserRegistrationCommandHandler(
UserRegistrationRepository userRegistrationRepository,
UserRepository userRepository) {
this.userRegistrationRepository = userRegistrationRepository;
this.userRepository = userRepository;
}
@Override
public BaseUserDto handle(ConfirmUserRegistrationCommand command) {
UserRegistration userRegistration = userRegistrationRepository.findByConfirmationCode(command.getConfirmationLink());
if (userRegistration == null) {
throw new IllegalStateException(
String.format("UserRegistration with confirmation link %s cannot be found.", command.getConfirmationLink())
);
}
User user = userRegistration.createUser(userRepository);
userRegistration.setUserId(user.getUserId());
userRepository.save(user);
userRegistrationRepository.save(userRegistration);
return null;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/GetUserRegistrationQueryHandler.java
================================================
package com.tomo.mcauthentication.application.registration;
import com.tomo.mcauthentication.application.configuration.QueryHandler;
import com.tomo.mcauthentication.application.registration.dto.UserRegistrationDto;
import com.tomo.mcauthentication.application.registration.query.GetUserRegistrationQuery;
public class GetUserRegistrationQueryHandler implements QueryHandler {
@Override public UserRegistrationDto handle(GetUserRegistrationQuery request) {
return null;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/NewUserRegisteredEventHandler.java
================================================
package com.tomo.mcauthentication.application.registration;
import com.tomo.mcauthentication.application.contracts.McAuthenticationModule;
import com.tomo.mcauthentication.application.registration.command.SendRegistrationConfirmationEmailCommand;
import com.tomo.ddd.domain.DomainEventPublisher;
import com.tomo.ddd.domain.DomainEventSubscriber;
import com.tomo.mcauthentication.domain.registration.events.UserRegistrationRequested;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class NewUserRegisteredEventHandler {
private McAuthenticationModule authenticationModule;
/**
* In order to not mix business logic with plugins like a GUI..
* baseUrl + URI + queryString eg. localhost/register/confirm/?confirmationCode=
*/
private String confirmationLink;
public NewUserRegisteredEventHandler(McAuthenticationModule authenticationModule, String confirmationLink) {
this.authenticationModule = authenticationModule;
this.confirmationLink = confirmationLink;
}
@Before(value = "execution(public * com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler.*(..)) && target(com.tomo.mcauthentication.application.registration.RegisterNewUserCommandHandler))")
public void listen() {
DomainEventPublisher
.instance()
.subscribe(new DomainEventSubscriber() {
public void handleEvent(UserRegistrationRequested aDomainEvent) {
authenticationModule.executeCommand(new SendRegistrationConfirmationEmailCommand(
aDomainEvent.getEmail(),
confirmationLink,
aDomainEvent.getConfirmationCode()));
}
public Class subscribedToEventType() {
return UserRegistrationRequested.class; // all domain events
}
});
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/RegisterNewUserCommandHandler.java
================================================
package com.tomo.mcauthentication.application.registration;
import com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler;
import com.tomo.mcauthentication.application.registration.command.RegisterNewUserCommand;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.users.UserRepository;
import org.springframework.stereotype.Component;
@Component
public class RegisterNewUserCommandHandler extends AbstractVoidyCommandHandler {
UserRegistrationRepository userRegistrationRepository;
UserRepository userRepository;
public RegisterNewUserCommandHandler(
UserRegistrationRepository userRegistrationRepository,
UserRepository userRepository) {
this.userRegistrationRepository = userRegistrationRepository;
this.userRepository = userRepository;
}
@Override
protected void abstractHandle(RegisterNewUserCommand aCommand) {
UserRegistration userRegistration = UserRegistration.registerNewUser(
aCommand.getPassword(),
aCommand.getEmail(),
aCommand.getFirstName(),
aCommand.getLastName()
);
userRegistrationRepository.save(userRegistration);
//todo send email
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/SendRegistrationConfirmationEmailCommandHandler.java
================================================
package com.tomo.mcauthentication.application.registration;
import com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler;
import com.tomo.mcauthentication.application.registration.command.SendRegistrationConfirmationEmailCommand;
import com.tomo.ddd.email.EmailSender;
import com.tomo.ddd.port.adapter.message.email.EmailMessage;
import org.springframework.stereotype.Component;
@Component
public class SendRegistrationConfirmationEmailCommandHandler extends AbstractVoidyCommandHandler {
EmailSender emailMessageSender;
public SendRegistrationConfirmationEmailCommandHandler(EmailSender emailMessageSender) {
this.emailMessageSender = emailMessageSender;
}
@Override
protected void abstractHandle(SendRegistrationConfirmationEmailCommand aCommand) {
//todo maybe validate if exist
this.emailMessageSender.send(new EmailMessage(
aCommand.getEmail(), ""
, "Confirm registration",
"Click on this confirmation link " + aCommand.getConfirmLink() + aCommand.getConfirmationCode()));
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/command/ChangePasswordCommand.java
================================================
package com.tomo.mcauthentication.application.registration.command;
import com.tomo.mcauthentication.application.contracts.BaseCommand;
import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class ChangePasswordCommand extends BaseCommand {
String accessToken;
@NotNull
private String oldPassword;
@NotNull
private String newPassword;
@NotNull
private String newPasswordRepeated;
public ChangePasswordCommand(String accessToken, String oldPassword, String newPassword, String newPasswordRepeated) {
this.accessToken = accessToken;
this.oldPassword = oldPassword;
this.newPassword = newPassword;
this.newPasswordRepeated = newPasswordRepeated;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/command/ConfirmUserRegistrationCommand.java
================================================
package com.tomo.mcauthentication.application.registration.command;
import com.tomo.mcauthentication.application.contracts.BaseCommand;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
public class ConfirmUserRegistrationCommand extends BaseCommand {
private String confirmationLink;
public ConfirmUserRegistrationCommand(String confirmLink) {
this.confirmationLink = confirmLink;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/command/RegisterNewUserCommand.java
================================================
package com.tomo.mcauthentication.application.registration.command;
import com.tomo.mcauthentication.application.contracts.BaseCommand;
import javax.validation.constraints.NotNull;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class RegisterNewUserCommand extends BaseCommand {
@NotNull
String firstName;
@NotNull
String lastName;
@NotNull
String email;
@NotNull
String password;
public RegisterNewUserCommand(String firstName, String lastName, String email, String password) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/command/SendRegistrationConfirmationEmailCommand.java
================================================
package com.tomo.mcauthentication.application.registration.command;
import com.tomo.mcauthentication.application.contracts.BaseCommand;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class SendRegistrationConfirmationEmailCommand extends BaseCommand {
String email;
String confirmLink;
String confirmationCode;
public SendRegistrationConfirmationEmailCommand(String email, String confirmLink, String confirmationCode) {
this.email = email;
this.confirmLink = confirmLink;
this.confirmationCode = confirmationCode;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/dto/UserRegistrationDto.java
================================================
package com.tomo.mcauthentication.application.registration.dto;
import com.tomo.mcauthentication.application.contracts.Response;
import com.tomo.mcauthentication.domain.registration.UserRegistrationId;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class UserRegistrationDto implements Response {
private String email;
private String firstName;
private String lastName;
private LocalDateTime registerDate;
private UserRegistrationStatus status;
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/registration/query/GetUserRegistrationQuery.java
================================================
package com.tomo.mcauthentication.application.registration.query;
import com.tomo.mcauthentication.application.contracts.BaseQuery;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class GetUserRegistrationQuery extends BaseQuery {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/users/ChangeUserDetailsCommandHandler.java
================================================
package com.tomo.mcauthentication.application.users;
import com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler;
import com.tomo.mcauthentication.application.users.command.ChangeUserDetailsCommand;
import com.tomo.mcauthentication.domain.registration.EmailAuthenticationService;
import com.tomo.mcauthentication.domain.session.TokenProvider;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
import com.tomo.mcauthentication.domain.users.UserRepository;
import org.springframework.stereotype.Service;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class ChangeUserDetailsCommandHandler extends AbstractVoidyCommandHandler {
EmailAuthenticationService emailAuthenticationService;
TokenProvider tokenProvider;
UserRepository userRepository;
@Override
public void abstractHandle(ChangeUserDetailsCommand aCommand) {
UserId userId = tokenProvider.getUserIdFromToken(aCommand.getAuthToken());
if (!aCommand.getUserId().equals(userId.id())) {
throw new IllegalArgumentException(String.format("Forbidden access. User with ID %s cannot change user details for user with ID: %s", userId.id(), aCommand.getUserId()));
}
User user = userRepository.findById(new UserId(aCommand.getUserId()));
user.updateDetails(aCommand.getFirstName(), aCommand.getLastName());
userRepository.save(user);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/users/GetUserQueryHandler.java
================================================
package com.tomo.mcauthentication.application.users;
import com.tomo.mcauthentication.application.BaseMapper;
import com.tomo.mcauthentication.application.configuration.QueryHandler;
import com.tomo.mcauthentication.application.users.dto.BaseUserDto;
import com.tomo.mcauthentication.application.users.query.GetUserQuery;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserRepository;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Component;
@Component
public class GetUserQueryHandler extends BaseMapper implements QueryHandler {
private UserRepository userRepository;
public GetUserQueryHandler(
UserRepository userRepository,
ModelMapper modelMapper) {
super(modelMapper);
this.userRepository = userRepository;
}
@Override
public BaseUserDto handle(GetUserQuery query) {
User user = userRepository.findById(query.getUserId());
if (user == null) {
throw new IllegalStateException(String.format("User with id %s doesn't exists.", query.getUserId().toString()));
}
return toDto(user);
}
private BaseUserDto toDto(User user) {
return modelMapper.map(user, BaseUserDto.class);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/users/command/ChangeUserDetailsCommand.java
================================================
package com.tomo.mcauthentication.application.users.command;
import com.tomo.mcauthentication.application.contracts.security.AbstractAuthorizeCommand;
import java.util.List;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class ChangeUserDetailsCommand extends AbstractAuthorizeCommand {
private UUID userId;
private String firstName;
private String lastName;
public ChangeUserDetailsCommand(UUID userId, String firstName, String lastName) {
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public List getAuthorities() {
List authorities = super.getAuthorities();
authorities.add("ADMIN");
return authorities;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/users/dto/BaseUserDto.java
================================================
package com.tomo.mcauthentication.application.users.dto;
import com.tomo.mcauthentication.application.contracts.Response;
import com.tomo.mcauthentication.domain.users.UserId;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class BaseUserDto implements Response {
private String userId;
String firstName;
String lastName;
public void setUserId(UserId userId) {
this.userId = userId.id().toString();
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/application/users/query/GetUserQuery.java
================================================
package com.tomo.mcauthentication.application.users.query;
import com.tomo.mcauthentication.application.contracts.security.AbstractAuthenticateQuery;
import com.tomo.mcauthentication.domain.users.UserId;
import javax.validation.constraints.NotNull;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class GetUserQuery extends AbstractAuthenticateQuery {
@NotNull
String userId;
public GetUserQuery(String userId) {
this.userId = userId;
}
public UserId getUserId() {
return new UserId(UUID.fromString(this.userId));
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/DomainRegistry.java
================================================
package com.tomo.mcauthentication.domain;
import com.tomo.mcauthentication.domain.registration.PasswordService;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.registration.rules.UserRegistrationMustBeUnique;
import com.tomo.mcauthentication.domain.session.TokenProvider;
import com.tomo.mcauthentication.domain.users.UserRepository;
import com.tomo.mcauthentication.domain.users.rules.UserEmailMustBeUnique;
import org.modelmapper.ModelMapper;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class DomainRegistry implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static EncryptionService encryptionService() {
return (EncryptionService) applicationContext.getBean("MD5EncryptionService");
}
public static PasswordService passwordService() {
return (PasswordService) applicationContext.getBean("passwordService");
}
public static TokenProvider tokenProvider() {
return (TokenProvider) applicationContext.getBean("jwtTokenProvider");
}
public static ModelMapper modelMapper() {
return (ModelMapper) applicationContext.getBean("modelMapper");
}
public static UserRepository userRepository() {
return (UserRepository) applicationContext.getBean("userRepository");
}
public static UserRegistrationRepository userRegistrationRepository() {
return (UserRegistrationRepository) applicationContext.getBean("userRegistrationRepository");
}
public static UserEmailMustBeUnique userEmailMustBeUnique(String anEmail) {
return new UserEmailMustBeUnique(userRepository(), anEmail);
}
public static UserRegistrationMustBeUnique userRegistrationMustBeUnique(String anEmail) {
return new UserRegistrationMustBeUnique(userRegistrationRepository(), anEmail);
}
@Override
public void setApplicationContext(ApplicationContext anApplicationContext) throws BeansException {
// if (DomainRegistry.applicationContext == null) {
// DomainRegistry.applicationContext = anApplicationContext;
// }
DomainRegistry.applicationContext = anApplicationContext;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/EncryptionService.java
================================================
package com.tomo.mcauthentication.domain;
public interface EncryptionService {
String encryptedValue(String aPlainTextValue);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/oauth2/OAuth2Authentication.java
================================================
package com.tomo.mcauthentication.domain.oauth2;
public interface OAuth2Authentication {
OAuth2Principal authenticate(String anAccessCode);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/oauth2/OAuth2Principal.java
================================================
package com.tomo.mcauthentication.domain.oauth2;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class OAuth2Principal {
private String id;
private String email;
private String firstName;
private String lastName;
private String imageUrl;
private String provider;
public OAuth2Principal(String id, String email, String firstName, String lastName, String imageUrl, String provider) {
this.id = id;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
this.imageUrl = imageUrl;
this.provider = provider;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/oauth2/OAuth2Service.java
================================================
package com.tomo.mcauthentication.domain.oauth2;
import com.tomo.ddd.domain.BusinessRuleValidationException;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserRepository;
import com.tomo.mcauthentication.domain.users.rules.UserEmailMustBeUnique;
public class OAuth2Service {
private OAuth2Authentication oAuth2Authentication;
private UserRepository userRespository;
public OAuth2Service(OAuth2Authentication oAuth2Authentication, UserRepository userRespository) {
this.oAuth2Authentication = oAuth2Authentication;
this.userRespository = userRespository;
}
public User registerAuthenticate(String anAccessCode) {
OAuth2Principal principal = oAuth2Authentication.authenticate(anAccessCode);
User user;
try {
user = authenticateAndRegister(principal);
} catch (BusinessRuleValidationException exception) {
if (UserEmailMustBeUnique.class == exception.getBrokenRule().getClass()) {
user = userRespository.findByEmail(principal.getEmail());
if (!User.AuthProvider.valueOf(principal.getProvider()).equals(user.getProvider())) {
throw exception;
}
} else {
throw exception;
}
}
return user;
}
protected User authenticateAndRegister(OAuth2Principal principal) {
User user = new User(
userRespository.nextIdentity(),
principal.getFirstName(),
principal.getLastName(),
principal.getEmail(),
User.AuthProvider.valueOf(principal.getProvider()));
return userRespository.save(user);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/EmailAuthenticationService.java
================================================
package com.tomo.mcauthentication.domain.registration;
import com.tomo.ddd.domain.BusinessRuleValidator;
import com.tomo.mcauthentication.domain.EncryptionService;
import com.tomo.mcauthentication.domain.registration.rules.PasswordsMustMatch;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserRepository;
import org.springframework.stereotype.Service;
@Service
public class EmailAuthenticationService extends BusinessRuleValidator {
UserRegistrationRepository userRegistrationRepository;
UserRepository userRepository;
EncryptionService encryptionService;
public EmailAuthenticationService(
UserRegistrationRepository userRegistrationRepository,
UserRepository userRepository,
EncryptionService encryptionService) {
this.userRegistrationRepository = userRegistrationRepository;
this.userRepository = userRepository;
this.encryptionService = encryptionService;
}
public User authenticate(
String anEmail,
String aPassword) {
this.assertArgumentNotEmpty(anEmail, "Email must be provided.");
this.assertArgumentNotEmpty(aPassword, "Password must be provided.");
UserRegistration userRegistration = userRegistrationRepository.findByEmail(anEmail);
if (userRegistration == null) {
throw new IllegalStateException(String.format("User with email %s doesn't exists.", anEmail));
}
this.checkRule(new PasswordsMustMatch(userRegistration.getPassword(), encryptionService.encryptedValue(aPassword)));
return userRepository.findByEmail(anEmail);
}
public String createPasswordRecoveryCode(String anEmail) {
this.assertArgumentNotEmpty(anEmail, "Email must be provided.");
UserRegistration userRegistration = userRegistrationRepository.findByEmail(anEmail);
if (userRegistration == null) {
throw new IllegalStateException(String.format("User with email %s doesn't exists.", anEmail));
}
return userRegistration.createRecoveryCode();
}
public void recoverPasswordWithRecoveryCode(String aRecoveryCode, String aNewPassword, String aNewPasswordRepeated) {
this.assertArgumentNotNull(aRecoveryCode, "Recovery code is missing.");
UserRegistration userRegistration = userRegistrationRepository
.findByRecoveryCode(encryptionService.encryptedValue(aRecoveryCode));
if (userRegistration == null) {
throw new IllegalStateException(String.format("User with recovery code %s doesn't exists.", aRecoveryCode));
}
userRegistration.changePasswordWithRecoveryCode(aRecoveryCode, aNewPassword, aNewPasswordRepeated);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/PasswordService.java
================================================
package com.tomo.mcauthentication.domain.registration;
import com.tomo.ddd.AssertionConcern;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component
public final class PasswordService extends AssertionConcern {
private static final String DIGITS = "0123456789";
private static final String LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final int STRONG_THRESHOLD = 20;
private static final String SYMBOLS = "\"`!?$?%^&*()_-+={[}]:;@'~#|\\<,>.?/";
private static final int VERY_STRONG_THRESHOLD = 40;
public PasswordService() {
super();
}
public String generateStrongPassword() {
String generatedPassword = null;
StringBuffer password = new StringBuffer();
Random random = new Random();
boolean isStrong = false;
int index = 0;
while (!isStrong) {
int opt = random.nextInt(4);
switch (opt) {
case 0:
index = random.nextInt(LETTERS.length());
password.append(LETTERS.substring(index, index+1));
break;
case 1:
index = random.nextInt(LETTERS.length());
password.append(LETTERS.substring(index, index+1).toLowerCase());
break;
case 2:
index = random.nextInt(DIGITS.length());
password.append(DIGITS.substring(index, index+1));
break;
case 3:
index = random.nextInt(SYMBOLS.length());
password.append(SYMBOLS.substring(index, index+1));
break;
}
generatedPassword = password.toString();
if (generatedPassword.length() >= 7) {
isStrong = this.isStrong(generatedPassword);
}
}
return generatedPassword;
}
public boolean isStrong(String aPlainTextPassword) {
return this.calculatePasswordStrength(aPlainTextPassword) >= STRONG_THRESHOLD;
}
public boolean isVeryStrong(String aPlainTextPassword) {
return this.calculatePasswordStrength(aPlainTextPassword) >= VERY_STRONG_THRESHOLD;
}
public boolean isWeak(String aPlainTextPassword) {
return this.calculatePasswordStrength(aPlainTextPassword) < STRONG_THRESHOLD;
}
private int calculatePasswordStrength(String aPlainTextPassword) {
this.assertArgumentNotNull(aPlainTextPassword, "Password strength cannot be tested on null.");
int strength = 0;
int length = aPlainTextPassword.length();
if (length > 7) {
strength += 10;
// bonus: one point each additional
strength += (length - 7);
}
int digitCount = 0;
int letterCount = 0;
int lowerCount = 0;
int upperCount = 0;
int symbolCount = 0;
for (int idx = 0; idx < length; ++idx) {
char ch = aPlainTextPassword.charAt(idx);
if (Character.isLetter(ch)) {
++letterCount;
if (Character.isUpperCase(ch)) {
++upperCount;
} else {
++lowerCount;
}
} else if (Character.isDigit(ch)) {
++digitCount;
} else {
++symbolCount;
}
}
strength += (upperCount + lowerCount + symbolCount);
// bonus: letters and digits
if (letterCount >= 2 && digitCount >= 2) {
strength += (letterCount + digitCount);
}
return strength;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/UserRegistration.java
================================================
package com.tomo.mcauthentication.domain.registration;
import com.tomo.ddd.domain.ConcurrencySafeEntity;
import com.tomo.ddd.domain.DomainEvent;
import com.tomo.ddd.domain.DomainEventPublisher;
import com.tomo.mcauthentication.domain.DomainRegistry;
import com.tomo.mcauthentication.domain.registration.events.PasswordChanged;
import com.tomo.mcauthentication.domain.registration.events.PasswordRecovered;
import com.tomo.mcauthentication.domain.registration.events.PasswordRecoveryCodeCreated;
import com.tomo.mcauthentication.domain.registration.events.UserRegistrationConfirmed;
import com.tomo.mcauthentication.domain.registration.events.UserRegistrationRequested;
import com.tomo.mcauthentication.domain.registration.rules.PasswordRecoveryCodeShouldBeExpiredOrNull;
import com.tomo.mcauthentication.domain.registration.rules.PasswordRecoveryCodeShouldNotExpired;
import com.tomo.mcauthentication.domain.registration.rules.PasswordsMustMatch;
import com.tomo.mcauthentication.domain.registration.rules.RecoveryCodeMustMatch;
import com.tomo.mcauthentication.domain.registration.rules.UserRegistrationCannotBeConfirmedAfterExpiration;
import com.tomo.mcauthentication.domain.registration.rules.UserRegistrationCannotBeConfirmedMoreThanOnce;
import com.tomo.mcauthentication.domain.registration.rules.UserRegistrationMustBeConfirmed;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
import com.tomo.mcauthentication.domain.users.UserRepository;
import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@NoArgsConstructor
@Getter
@Setter
public class UserRegistration extends ConcurrencySafeEntity {
public static Long RECOVERY_CODE_EXPIREATION_MSEC = 172800000L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String password;
private String email;
private String firstName;
private String lastName;
private String confirmationCode;
private LocalDateTime registerDate;
@Enumerated(EnumType.STRING)
private UserRegistrationStatus status;
private String recoveryCode;
private LocalDateTime recoveryCodeExpirationDate;
@Embedded
@AttributeOverride(name="id", column = @Column(name="user_id"))
private UserId userId;
public static UserRegistration registerNewUser(
String password,
String email,
String firstName,
String lastName)
{
return new UserRegistration(password, email, firstName, lastName);
}
private UserRegistration(
String aPassword,
String anEmail,
String aFirstName,
String aLastName) {
this.checkRule(DomainRegistry.userRegistrationMustBeUnique(anEmail));
this.checkRule(DomainRegistry.userEmailMustBeUnique(anEmail));
this.email = anEmail;
this.firstName = aFirstName;
this.lastName = aLastName;
this.confirmationCode = UUID.randomUUID().toString();
this.registerDate = LocalDateTime.now();
this.status = UserRegistrationStatus.WaitingForConfirmation;
this.protectPassword("", aPassword);
this.publishEvent(new UserRegistrationRequested(
this.email,
this.confirmationCode,
this.firstName,
this.lastName,
this.registerDate,
this.status
));
}
public User createUser(UserRepository userRespository) {
this.checkRule(new UserRegistrationCannotBeConfirmedMoreThanOnce(this.status));
this.checkRule(new UserRegistrationCannotBeConfirmedAfterExpiration(this.registerDate));
this.setStatus(UserRegistrationStatus.Confirmed);
UserId userId = userRespository.nextIdentity();
this.setUserId(userId);
this.publishEvent(new UserRegistrationConfirmed(this.id, this.getStatus(), this.getUserId()));
return new User(userId, getFirstName(), getLastName(), getEmail(), User.AuthProvider.EMAIL);
}
public String createRecoveryCode() {
this.checkRule(new UserRegistrationMustBeConfirmed(this.getStatus()));
this.checkRule(new PasswordRecoveryCodeShouldBeExpiredOrNull(this));
String recoveryCode = UUID.randomUUID().toString();
this.recoveryCodeExpirationDate = LocalDateTime.now().plus(RECOVERY_CODE_EXPIREATION_MSEC, ChronoField.MILLI_OF_DAY.getBaseUnit());
this.setRecoveryCodeExpirationDate(recoveryCodeExpirationDate);
this.setRecoveryCode(this.asEncryptedValue(recoveryCode));
this.publishEvent(new PasswordRecoveryCodeCreated(
this.id,
this.email,
recoveryCode,
this.recoveryCodeExpirationDate));
return recoveryCode;
}
public boolean isRecoveryCodeUnexpired() {
return recoveryCodeExpirationDate != null && recoveryCodeExpirationDate.isAfter(LocalDateTime.now());
}
public boolean isRecoveryCodeExpired() {
return recoveryCodeExpirationDate != null && recoveryCodeExpirationDate.isBefore(LocalDateTime.now());
}
public void changePassword(String anOldPassword, String aNewPassword, String aNewPasswordRepeated) {
this.assertArgumentNotNull(anOldPassword, "Old password is missing.");
this.assertNewPassword(aNewPassword, aNewPasswordRepeated);
this.checkRule(new PasswordsMustMatch(this.getPassword(), this.asEncryptedValue(anOldPassword)));
this.protectPassword(this.getPassword(), aNewPassword);
this.publishEvent(new PasswordChanged(this.id, this.password));
}
public void changePasswordWithRecoveryCode(String aRecoveryCode, String aNewPassword, String aNewPasswordRepeated) {
this.assertArgumentNotNull(aNewPassword, "New password is missing.");
this.assertNewPassword(aNewPassword, aNewPasswordRepeated);
this.checkRule(new RecoveryCodeMustMatch(this.asEncryptedValue(aRecoveryCode), this.getRecoveryCode()));
this.checkRule(new PasswordRecoveryCodeShouldNotExpired(this));
this.protectPassword(this.getPassword(), aNewPassword);
this.setRecoveryCodeExpirationDate(LocalDateTime.now());
this.publishEvent(new PasswordRecovered(
this.id,
this.getPassword(),
this.getRecoveryCode()
));
}
protected void assertNewPassword(String aNewPassword, String aNewPasswordRepeated) {
this.assertArgumentNotNull(aNewPassword, "New password is missing.");
this.assertArgumentNotNull(aNewPasswordRepeated, "Repeated password is missing.");
this.assertArgumentEquals(aNewPassword, aNewPasswordRepeated, "Provided passwords must be equal.");
}
protected void protectPassword(String aCurrentPassword, String aChangedPassword) {
this.assertPasswordsNotSame(aCurrentPassword, this.asEncryptedValue(aChangedPassword));
this.assertPasswordNotWeak(aChangedPassword);
this.assertUsernamePasswordNotSame(aChangedPassword);
this.setPassword(this.asEncryptedValue(aChangedPassword));
}
protected void assertPasswordsNotSame(String aCurrentPassword, String aChangedPassword) {
this.assertArgumentNotEquals(
aCurrentPassword,
aChangedPassword,
"The password is unchanged.");
}
protected void assertPasswordNotWeak(String aPlainTextPassword) {
this.assertArgumentFalse(
DomainRegistry.passwordService().isWeak(aPlainTextPassword),
"The password must be stronger.");
}
protected void assertUsernamePasswordNotSame(String aPlainTextPassword) {
this.assertArgumentNotEquals(
this.getEmail(),
aPlainTextPassword,
"The username and password must not be the same.");
}
protected String asEncryptedValue(String aPlainTextPassword) {
String encryptedValue =
DomainRegistry
.encryptionService()
.encryptedValue(aPlainTextPassword);
return encryptedValue;
}
protected void setPassword(String aPassword) {
this.password = aPassword;
}
private void publishEvent(DomainEvent domainEvent) {
DomainEventPublisher.instance().publish(domainEvent);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/UserRegistrationId.java
================================================
package com.tomo.mcauthentication.domain.registration;
import com.tomo.ddd.domain.AbstractId;
import javax.persistence.Embeddable;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Embeddable
@Getter
@Setter
@NoArgsConstructor
public class UserRegistrationId extends AbstractId {
public UserRegistrationId(UUID anId) {
super(anId);
}
@Override
protected int hashPrimeValue() {
return 0;
}
@Override
protected int hashOddValue() {
return 0;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/UserRegistrationRepository.java
================================================
package com.tomo.mcauthentication.domain.registration;
import com.tomo.ddd.domain.BaseRepository;
import com.tomo.mcauthentication.domain.users.UserId;
import java.util.List;
public interface UserRegistrationRepository extends BaseRepository {
List findAllByEmail(List emails);
UserRegistration findByEmail(String anEmail);
long countByEmailAndStatus(String email, UserRegistrationStatus status);
UserRegistration findByConfirmationCode(String confirmationCode);
UserRegistration findByRecoveryCode(String aRecoveryCode);
UserRegistration findByUserId(UserId anUserId);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/UserRegistrationStatus.java
================================================
package com.tomo.mcauthentication.domain.registration;
public enum
UserRegistrationStatus {
WaitingForConfirmation("WaitingForConfirmation"),
Confirmed("Confirmed"),
Expired("Expired");
String value;
UserRegistrationStatus(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/UsersCounter.java
================================================
package com.tomo.mcauthentication.domain.registration;
public interface UsersCounter {
int countUsersWithLogin();
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/events/PasswordChanged.java
================================================
package com.tomo.mcauthentication.domain.registration.events;
import com.tomo.ddd.domain.BaseDomainEvent;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class PasswordChanged extends BaseDomainEvent {
private long userRegistrationId;
private String password; //encrypted
public PasswordChanged(long userRegistrationId, String password) {
this.userRegistrationId = userRegistrationId;
this.password = password;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/events/PasswordRecovered.java
================================================
package com.tomo.mcauthentication.domain.registration.events;
import com.tomo.ddd.domain.BaseDomainEvent;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class PasswordRecovered extends BaseDomainEvent {
private long userRegistrationId;
private String password; //encrypted
private String recoveryCode; //encrypted
public PasswordRecovered(long userRegistrationId, String password, String recoveryCode) {
this.userRegistrationId = userRegistrationId;
this.password = password;
this.recoveryCode = recoveryCode;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/events/PasswordRecoveryCodeCreated.java
================================================
package com.tomo.mcauthentication.domain.registration.events;
import com.tomo.ddd.domain.BaseDomainEvent;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class PasswordRecoveryCodeCreated extends BaseDomainEvent {
long userRegistrationId;
private String email;
private String recoveryCode;
private LocalDateTime recoveryCodeExpirationDate;
public PasswordRecoveryCodeCreated(long userRegistrationId, String email, String recoveryCode, LocalDateTime recoveryCodeExpirationDate) {
this.userRegistrationId = userRegistrationId;
this.email = email;
this.recoveryCode = recoveryCode;
this.recoveryCodeExpirationDate = recoveryCodeExpirationDate;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/events/UserRegistrationConfirmed.java
================================================
package com.tomo.mcauthentication.domain.registration.events;
import com.tomo.ddd.domain.BaseDomainEvent;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
import com.tomo.mcauthentication.domain.users.UserId;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class UserRegistrationConfirmed extends BaseDomainEvent {
private long userRegistrationId;
private UserRegistrationStatus status;
private UserId userId;
public UserRegistrationConfirmed(long userRegistrationId, UserRegistrationStatus status, UserId userId) {
this.userRegistrationId = userRegistrationId;
this.status = status;
this.userId = userId;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/events/UserRegistrationRequested.java
================================================
package com.tomo.mcauthentication.domain.registration.events;
import com.tomo.ddd.domain.BaseDomainEvent;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class UserRegistrationRequested extends BaseDomainEvent {
private long userRegistrationId;
private String email;
private String confirmationCode;
private String firstName;
private String lastName;
private LocalDateTime registerDate;
private UserRegistrationStatus status;
public UserRegistrationRequested(
String email,
String confirmationCode,
String firstName,
String lastName,
LocalDateTime registerDate,
UserRegistrationStatus status) {
this.email = email;
this.confirmationCode = confirmationCode;
this.firstName = firstName;
this.lastName = lastName;
this.registerDate = registerDate;
this.status = status;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/rules/PasswordRecoveryCodeShouldBeExpiredOrNull.java
================================================
package com.tomo.mcauthentication.domain.registration.rules;
import com.tomo.ddd.domain.BusinessRule;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
public class PasswordRecoveryCodeShouldBeExpiredOrNull implements BusinessRule {
private UserRegistration userRegistration;
public PasswordRecoveryCodeShouldBeExpiredOrNull(UserRegistration aUserRegistration) {
this.userRegistration = aUserRegistration;
}
@Override
public Boolean isRuleComplied() {
return userRegistration.isRecoveryCodeExpired() || userRegistration.getRecoveryCode() == null;
}
@Override
public String message() {
return "User recovery code is not expired yet. You can't get new one after the current code expire.";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/rules/PasswordRecoveryCodeShouldNotExpired.java
================================================
package com.tomo.mcauthentication.domain.registration.rules;
import com.tomo.ddd.domain.BusinessRule;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
public class PasswordRecoveryCodeShouldNotExpired implements BusinessRule {
private UserRegistration userRegistration;
public PasswordRecoveryCodeShouldNotExpired(UserRegistration aUserRegistration) {
this.userRegistration = aUserRegistration;
}
@Override
public Boolean isRuleComplied() {
return userRegistration.isRecoveryCodeUnexpired();
}
@Override
public String message() {
return "User recovery code is not expired yet. You can't get new one after the current code expire.";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/rules/PasswordsMustMatch.java
================================================
package com.tomo.mcauthentication.domain.registration.rules;
import com.tomo.ddd.domain.BusinessRule;
public class PasswordsMustMatch implements BusinessRule {
private String providedPassoword;
private String storedPassword;
public PasswordsMustMatch(String storedPassword, String providedPassoword) {
this.storedPassword = storedPassword;
this.providedPassoword = providedPassoword;
}
@Override
public Boolean isRuleComplied() {
return providedPassoword.equals(storedPassword);
}
@Override
public String message() {
return "Passwords dont match.";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/rules/RecoveryCodeMustMatch.java
================================================
package com.tomo.mcauthentication.domain.registration.rules;
import com.tomo.ddd.domain.BusinessRule;
public class RecoveryCodeMustMatch implements BusinessRule {
private String providedCode;
private String storedCode;
public RecoveryCodeMustMatch(String aStoredCode, String aProvidedCode) {
this.storedCode = aStoredCode;
this.providedCode = aProvidedCode;
}
@Override
public Boolean isRuleComplied() {
return providedCode.equals(storedCode);
}
@Override
public String message() {
return "Passwords dont match.";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/rules/UserRegistrationCannotBeConfirmedAfterExpiration.java
================================================
package com.tomo.mcauthentication.domain.registration.rules;
import com.tomo.ddd.domain.BusinessRule;
import java.time.LocalDateTime;
public class UserRegistrationCannotBeConfirmedAfterExpiration implements BusinessRule {
public static final int CONFIRMATION_LINK_DURATION = 8;
LocalDateTime registerDate;
public UserRegistrationCannotBeConfirmedAfterExpiration(LocalDateTime aRegisterDate) {
this.registerDate = aRegisterDate;
}
@Override
public Boolean isRuleComplied() {
return LocalDateTime.now().isBefore(this.registerDate.plusDays(CONFIRMATION_LINK_DURATION));
}
@Override
public String message() {
return null;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/rules/UserRegistrationCannotBeConfirmedMoreThanOnce.java
================================================
package com.tomo.mcauthentication.domain.registration.rules;
import com.tomo.ddd.domain.BusinessRule;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
public class UserRegistrationCannotBeConfirmedMoreThanOnce implements BusinessRule {
UserRegistrationStatus status;
public UserRegistrationCannotBeConfirmedMoreThanOnce(UserRegistrationStatus aStatus) {
this.status = aStatus;
}
@Override
public Boolean isRuleComplied() {
return !this.status.equals(UserRegistrationStatus.Confirmed);
}
@Override
public String message() {
return "User Registration cannot be confirmed more than once";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/rules/UserRegistrationMustBeConfirmed.java
================================================
package com.tomo.mcauthentication.domain.registration.rules;
import com.tomo.ddd.domain.BusinessRule;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
public class UserRegistrationMustBeConfirmed implements BusinessRule {
private UserRegistrationStatus userRegistrationStatus;
public UserRegistrationMustBeConfirmed(UserRegistrationStatus anUserRegistrationStatus) {
this.userRegistrationStatus = anUserRegistrationStatus;
}
@Override
public Boolean isRuleComplied() {
return userRegistrationStatus.equals(UserRegistrationStatus.Confirmed);
}
@Override
public String message() {
return "User registration is not confirmed yet.";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/registration/rules/UserRegistrationMustBeUnique.java
================================================
package com.tomo.mcauthentication.domain.registration.rules;
import com.tomo.ddd.domain.BusinessRule;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
public class UserRegistrationMustBeUnique implements BusinessRule {
private UserRegistrationRepository repository;
private String email;
public UserRegistrationMustBeUnique(UserRegistrationRepository usersCounter, String email) {
this.repository = usersCounter;
this.email = email;
}
@Override
public Boolean isRuleComplied() {
return repository.countByEmailAndStatus(email, UserRegistrationStatus.Confirmed) < 1;
}
@Override
public String message() {
return "User login must be unique";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/session/JwtTokenProvider.java
================================================
package com.tomo.mcauthentication.domain.session;
import com.tomo.mcauthentication.domain.session.Session.TokenType;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
import com.tomo.mcauthentication.infrastructure.springboot.configuration.AppProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.util.Date;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
@Service
@Qualifier("jwtTokenProvider")
public class JwtTokenProvider implements TokenProvider {
private static final Logger logger = LoggerFactory.getLogger(TokenProvider.class);
private AppProperties appProperties;
public JwtTokenProvider(AppProperties appProperties) {
this.appProperties = appProperties;
}
@Override public String createToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + appProperties.getAuth().getTokenExpirationMsec());
return Jwts.builder()
.setSubject(user.getUserId().id().toString())
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(secretKey())
.compact();
}
@Override
public UserId getUserIdFromToken(String anAuthToken) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(secretKey())
.build()
.parseClaimsJws(anAuthToken)
.getBody();
return new UserId(claims.getSubject());
}
@Override
public boolean validateToken(String anAuthToken) {
try {
Jwts.parserBuilder()
.setSigningKey(secretKey())
.build()
.parseClaimsJws(anAuthToken);
return true;
} catch (SecurityException ex) {
logger.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token");
throw ex;
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty.");
}
return false;
}
@Override
public TokenType getTokenType() {
return TokenType.CLIENT_SECRET_JWT;
}
@Override public String createRefreshToken(User user) {
return null;
}
private SecretKey secretKey() {
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(appProperties.getAuth().getTokenSecret()));
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/session/Session.java
================================================
package com.tomo.mcauthentication.domain.session;
import com.tomo.ddd.domain.DomainEventPublisher;
import com.tomo.ddd.domain.RootEntity;
import com.tomo.mcauthentication.domain.DomainRegistry;
import com.tomo.mcauthentication.domain.session.events.SessionCreated;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@NoArgsConstructor
@Getter
@Setter
public class Session extends RootEntity {
public static Long EXPIRATION_MSEC = 15000L;
public enum TokenType {
CLIENT_SECRET_JWT,
PRIVATE_KEY_JWT,
BASIC,
API_KEY
}
@EmbeddedId
private SessionId sessionId;
private String accessToken;
private LocalDateTime expirationDate;
@Enumerated(EnumType.STRING)
private TokenType tokenType;
private String refreshToken;
private String userAgent;
private String ipAddress;
private LocalDateTime lastActivity;
@Embedded
@AttributeOverride(name="id", column = @Column(name="user_id"))
private UserId userId;
public Session(SessionId sessionId, User user, TokenProvider tokenProvider, Boolean rememberMe, String userAgent, String ipAddress)
{
this.sessionId = sessionId;
this.ipAddress = ipAddress;
this.userAgent = userAgent;
this.tokenType = tokenProvider.getTokenType();
this.userId = user.getUserId();
this.expirationDate = LocalDateTime.now().plus(EXPIRATION_MSEC, ChronoField.MILLI_OF_DAY.getBaseUnit());
this.accessToken = tokenProvider.createToken(user);
if (Boolean.TRUE.equals(rememberMe)) {
this.refreshToken = tokenProvider.createRefreshToken(user);
}
DomainEventPublisher.instance().publish(
new SessionCreated(this.getSessionId(), this.getUserId())
);
}
public boolean isExpired() {
return !expirationDate.isAfter(LocalDateTime.now());
}
private void protectedAccessToken(String anToken) {
this.assertArgumentNotEmpty(anToken, "Access token cannot be empty.");
this.setAccessToken(DomainRegistry.encryptionService().encryptedValue(anToken));
}
private void protectedRefreshToken(String anToken) {
this.assertArgumentNotEmpty(anToken, "Refresh token cannot be empty.");
this.setRefreshToken(DomainRegistry.encryptionService().encryptedValue(anToken));
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/session/SessionAuthenticationService.java
================================================
package com.tomo.mcauthentication.domain.session;
import com.tomo.ddd.domain.BusinessRuleValidator;
import com.tomo.mcauthentication.domain.session.rule.SessionCannotBeExpiredWhenRefreshTokenIsMissing;
import com.tomo.mcauthentication.domain.users.UserRepository;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class SessionAuthenticationService extends BusinessRuleValidator {
TokenProvider tokenProvider;
SessionRepository sessionRepository;
UserRepository userRepository;
public SessionAuthenticationService(
TokenProvider tokenProvider,
SessionRepository sessionRepository,
@Qualifier("userRepositoryJpaAdapter") UserRepository userRepository) {
this.tokenProvider = tokenProvider;
this.sessionRepository = sessionRepository;
this.userRepository = userRepository;
}
public Session authenticate(String anAccessToken) {
assertArgumentNotEmpty(anAccessToken, "Session token cannot be empty.");
Session session = sessionRepository.findByAccessToken(anAccessToken);
if (session == null) {
throw new IllegalStateException(String.format("Session with access code %s doesn't exist.", anAccessToken));
}
checkRule(new SessionCannotBeExpiredWhenRefreshTokenIsMissing(session));
if (session.isExpired()) {
return new Session(
sessionRepository.nextIdentity(),
userRepository.findById(session.getUserId()),
tokenProvider,
true,
session.getUserAgent(),
session.getIpAddress());
}
session.setLastActivity(LocalDateTime.now());
return session;
}
public Session logout(String anAccessToken) {
assertArgumentNotEmpty(anAccessToken, "Session token cannot be empty.");
Session session = sessionRepository.findByAccessToken(anAccessToken);
if (session == null) {
throw new IllegalStateException(String.format("Session with access code %s doesn't exist.", anAccessToken));
}
session.setExpirationDate(LocalDateTime.now());
session.setRefreshToken(null);
return sessionRepository.save(session);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/session/SessionId.java
================================================
package com.tomo.mcauthentication.domain.session;
import com.tomo.ddd.domain.AbstractId;
import javax.persistence.Embeddable;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Embeddable
@Getter
@Setter
@NoArgsConstructor
public class SessionId extends AbstractId {
public SessionId(UUID id) {
super(id);
}
@Override
protected int hashOddValue() {
return 5785;
}
@Override
protected int hashPrimeValue() {
return 31;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/session/SessionRepository.java
================================================
package com.tomo.mcauthentication.domain.session;
import com.tomo.ddd.domain.BaseRepository;
import com.tomo.mcauthentication.domain.users.UserId;
import java.util.List;
public interface SessionRepository extends BaseRepository {
SessionId nextIdentity();
List findByUserId(UserId userId);
Session findByAccessToken(String anAccessToken);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/session/TokenProvider.java
================================================
package com.tomo.mcauthentication.domain.session;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
public interface TokenProvider {
String createToken(User user);
String createRefreshToken(User user);
UserId getUserIdFromToken(String anAuthToken);
boolean validateToken(String anAuthToken);
Session.TokenType getTokenType();
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/session/events/SessionCreated.java
================================================
package com.tomo.mcauthentication.domain.session.events;
import com.tomo.ddd.domain.BaseDomainEvent;
import com.tomo.mcauthentication.domain.session.SessionId;
import com.tomo.mcauthentication.domain.users.UserId;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class SessionCreated extends BaseDomainEvent {
private SessionId sessionId;
private UserId userId;
public SessionCreated(SessionId sessionId, UserId userId) {
this.sessionId = sessionId;
this.userId = userId;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/session/rule/SessionCannotBeExpiredWhenRefreshTokenIsMissing.java
================================================
package com.tomo.mcauthentication.domain.session.rule;
import com.tomo.ddd.domain.BusinessRule;
import com.tomo.mcauthentication.domain.session.Session;
public class SessionCannotBeExpiredWhenRefreshTokenIsMissing implements BusinessRule {
Session session;
public SessionCannotBeExpiredWhenRefreshTokenIsMissing(Session session) {
this.session = session;
}
@Override
public Boolean isRuleComplied() {
return (!session.isExpired()) || (session.isExpired() && session.getRefreshToken() != null);
}
@Override
public String message() {
return String.format("Session token is expired and refresh token is missing.");
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/users/EmailLogin.java
================================================
package com.tomo.mcauthentication.domain.users;
public class EmailLogin {
User user;
String username;
String password;
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/users/User.java
================================================
package com.tomo.mcauthentication.domain.users;
import com.tomo.ddd.domain.ConcurrencySafeEntity;
import com.tomo.ddd.domain.DomainEvent;
import com.tomo.ddd.domain.DomainEventPublisher;
import com.tomo.mcauthentication.domain.DomainRegistry;
import com.tomo.mcauthentication.domain.users.events.UserCreated;
import com.tomo.mcauthentication.domain.users.events.UserNameChanged;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity(name = "mcuser")
@Getter
@NoArgsConstructor
public class User extends ConcurrencySafeEntity {
public enum AuthProvider {
EMAIL,
FACEBOOK,
GOOGLE
}
@EmbeddedId
UserId userId;
String firstName;
String lastName;
String email;
@Enumerated(EnumType.STRING)
AuthProvider provider;
public User(
UserId anId,
String aFirstName,
String aLastName,
String anEmail,
AuthProvider aProvider) {
this.checkRule(DomainRegistry.userEmailMustBeUnique(anEmail));
this.userId = anId;
this.firstName = aFirstName;
this.lastName = aLastName;
this.email = anEmail;
this.provider = aProvider;
this.publish(new UserCreated(
this.getUserId(),
this.getFirstName(),
this.getLastName(),
this.getEmail(),
this.getProvider()
));
}
public void updateDetails(String aFirstName, String aLastName) {
this.setFirstName(aFirstName);
this.setLastName(aLastName);
publish(new UserNameChanged(
this.getEmail(),
this.getFirstName(),
this.getLastName()));
}
public void setLastName(String aLastName) {
assertArgumentNotEmpty(lastName, "Last name cannot be empty.");
this.lastName = aLastName;
}
public void setFirstName(String aFirstName) {
assertArgumentNotEmpty(aFirstName, "First name cannot be empty.");
this.firstName = aFirstName;
}
private void publish(DomainEvent event) {
DomainEventPublisher.instance().publish(event);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/users/UserId.java
================================================
package com.tomo.mcauthentication.domain.users;
import com.tomo.ddd.domain.AbstractId;
import javax.persistence.Embeddable;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Embeddable
@Getter
@Setter
@NoArgsConstructor
public class UserId extends AbstractId {
public UserId(UUID id) {
super(id);
}
public UserId(String id) {
super(UUID.fromString(id));
}
@Override
protected int hashOddValue() {
return 83811;
}
@Override
protected int hashPrimeValue() {
return 263;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/users/UserRepository.java
================================================
package com.tomo.mcauthentication.domain.users;
import com.tomo.ddd.domain.BaseRepository;
public interface UserRepository extends BaseRepository {
UserId nextIdentity();
User findByEmail(String anEmail);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/users/events/UserCreated.java
================================================
package com.tomo.mcauthentication.domain.users.events;
import com.tomo.ddd.domain.BaseDomainEvent;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class UserCreated extends BaseDomainEvent {
UserId userId;
String firstName;
String lastName;
String email;
User.AuthProvider provider;
public UserCreated(UserId userId, String firstName, String lastName, String email, User.AuthProvider provider) {
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.provider = provider;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/users/events/UserNameChanged.java
================================================
package com.tomo.mcauthentication.domain.users.events;
import com.tomo.ddd.domain.BaseDomainEvent;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class UserNameChanged extends BaseDomainEvent {
String email;
String firstName;
String lastName;
public UserNameChanged(String email, String firstName, String lastName) {
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/domain/users/rules/UserEmailMustBeUnique.java
================================================
package com.tomo.mcauthentication.domain.users.rules;
import com.tomo.ddd.domain.BusinessRule;
import com.tomo.mcauthentication.domain.users.UserRepository;
public class UserEmailMustBeUnique implements BusinessRule {
UserRepository userRespository;
String email;
public UserEmailMustBeUnique(UserRepository userRespository, String email) {
this.userRespository = userRespository;
this.email = email;
}
@Override
public Boolean isRuleComplied() {
return userRespository.findByEmail(this.email) == null;
}
@Override
public String message() {
return "User with this email already exists.";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/McAuthenticationModuleExecutor.java
================================================
package com.tomo.mcauthentication.infrastructure;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.application.configuration.QueryHandler;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.McAuthenticationModule;
import com.tomo.mcauthentication.application.contracts.Query;
import com.tomo.mcauthentication.application.contracts.Response;
import com.tomo.mcauthentication.infrastructure.processing.builder.CommandHandlerPipelineBuilder;
import com.tomo.mcauthentication.infrastructure.processing.builder.QueryHandlerPipelineBuilder;
import org.springframework.stereotype.Component;
@Component
public class McAuthenticationModuleExecutor implements McAuthenticationModule {
CommandHandlerPipelineBuilder commandHandlerPipelineBuilder;
QueryHandlerPipelineBuilder queryHandlerPipelineBuilder;
public McAuthenticationModuleExecutor(
CommandHandlerPipelineBuilder commandHandlerPipelineBuilder,
QueryHandlerPipelineBuilder queryHandlerPipelineBuilder) {
this.commandHandlerPipelineBuilder = commandHandlerPipelineBuilder;
this.queryHandlerPipelineBuilder = queryHandlerPipelineBuilder;
}
@Override
public Response executeCommand(Command command) {
CommandHandler commandHandler = commandHandlerPipelineBuilder
.with(command)
.build();
return commandHandler.handle(command);
}
@Override
public Response executeQuery(Query query) {
QueryHandler queryHandler = queryHandlerPipelineBuilder
.with(query)
.build();
return queryHandler.handle(query);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/http/oauth2/AbstractOAuth2Authentication.java
================================================
package com.tomo.mcauthentication.infrastructure.http.oauth2;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Principal;
import com.tomo.mcauthentication.infrastructure.http.oauth2.user.OAuth2UserInfo;
import com.tomo.mcauthentication.infrastructure.http.oauth2.user.OAuth2UserInfoFactory;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.time.Instant;
public abstract class AbstractOAuth2Authentication {
ClientRegistration clientRegistration;
CustomOAuth2UserService customOAuth2UserService;
public AbstractOAuth2Authentication(ClientRegistration clientRegistration,
CustomOAuth2UserService customOAuth2UserService) {
this.clientRegistration = clientRegistration;
this.customOAuth2UserService = customOAuth2UserService;
}
protected OAuth2Principal authenticateUser(String anAccessCode) {
OAuth2UserRequest oAuth2UserRequest = new OAuth2UserRequest(clientRegistration, new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER,
anAccessCode,
Instant.now(), Instant.now().plusSeconds(10000L)));
OAuth2User oAuth2User = customOAuth2UserService.loadUser(oAuth2UserRequest);
OAuth2UserInfo userInfo = OAuth2UserInfoFactory
.getOAuth2UserInfo(clientRegistration.getRegistrationId(), oAuth2User.getAttributes());
return new OAuth2Principal(
userInfo.getId(),
userInfo.getEmail(),
userInfo.getName(),
userInfo.getName(),
userInfo.getImageUrl(),
clientRegistration.getRegistrationId());
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/http/oauth2/CustomOAuth2UserService.java
================================================
package com.tomo.mcauthentication.infrastructure.http.oauth2;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
return super.loadUser(oAuth2UserRequest);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/http/oauth2/FacebookOAuth2Authentication.java
================================================
package com.tomo.mcauthentication.infrastructure.http.oauth2;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Authentication;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Principal;
import com.tomo.mcauthentication.infrastructure.http.oauth2.user.FacebookOAuth2UserInfo;
import com.tomo.mcauthentication.infrastructure.http.oauth2.user.OAuth2UserInfoFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
@Service
public class FacebookOAuth2Authentication extends AbstractOAuth2Authentication implements OAuth2Authentication {
public FacebookOAuth2Authentication(
@Qualifier("facebookClientRegistration") ClientRegistration clientRegistration,
CustomOAuth2UserService customOAuth2UserService) {
super(clientRegistration, customOAuth2UserService);
}
@Override
public OAuth2Principal authenticate(String anAccessCode) {
return super.authenticateUser(anAccessCode);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/http/oauth2/GoogleOAuth2Authentication.java
================================================
package com.tomo.mcauthentication.infrastructure.http.oauth2;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Authentication;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Principal;
import com.tomo.mcauthentication.infrastructure.http.oauth2.user.FacebookOAuth2UserInfo;
import com.tomo.mcauthentication.infrastructure.http.oauth2.user.OAuth2UserInfoFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
@Service
public class GoogleOAuth2Authentication extends AbstractOAuth2Authentication implements OAuth2Authentication {
public GoogleOAuth2Authentication(
@Qualifier("googleClientRegistration") ClientRegistration clientRegistration,
CustomOAuth2UserService customOAuth2UserService) {
super(clientRegistration, customOAuth2UserService);
}
@Override
public OAuth2Principal authenticate(String anAccessCode) {
return super.authenticateUser(anAccessCode);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/http/oauth2/user/FacebookOAuth2UserInfo.java
================================================
package com.tomo.mcauthentication.infrastructure.http.oauth2.user;
import java.util.Map;
public class FacebookOAuth2UserInfo extends OAuth2UserInfo {
public FacebookOAuth2UserInfo(Map attributes) {
super(attributes);
}
@Override
public String getId() {
return (String) attributes.get("id");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getImageUrl() {
if(attributes.containsKey("picture")) {
Map pictureObj = (Map) attributes.get("picture");
if(pictureObj.containsKey("data")) {
Map dataObj = (Map) pictureObj.get("data");
if(dataObj.containsKey("url")) {
return (String) dataObj.get("url");
}
}
}
return null;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/http/oauth2/user/GoogleOAuth2UserInfo.java
================================================
package com.tomo.mcauthentication.infrastructure.http.oauth2.user;
import java.util.Map;
public class GoogleOAuth2UserInfo extends OAuth2UserInfo {
public GoogleOAuth2UserInfo(Map attributes) {
super(attributes);
}
@Override
public String getId() {
return (String) attributes.get("sub");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getImageUrl() {
return (String) attributes.get("picture");
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/http/oauth2/user/OAuth2UserInfo.java
================================================
package com.tomo.mcauthentication.infrastructure.http.oauth2.user;
import java.util.Map;;
public abstract class OAuth2UserInfo {
protected Map attributes;
public OAuth2UserInfo(Map attributes) {
this.attributes = attributes;
}
public Map getAttributes() {
return attributes;
}
public abstract String getId();
public abstract String getName();
public abstract String getEmail();
public abstract String getImageUrl();
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/http/oauth2/user/OAuth2UserInfoFactory.java
================================================
package com.tomo.mcauthentication.infrastructure.http.oauth2.user;
import com.tomo.mcauthentication.domain.users.User;
import java.util.Map;
public class OAuth2UserInfoFactory {
public static OAuth2UserInfo getOAuth2UserInfo(String registrationId, Map attributes) {
if(registrationId.equalsIgnoreCase(User.AuthProvider.GOOGLE.toString().toLowerCase())) {
return new GoogleOAuth2UserInfo(attributes);
} else if (registrationId.equalsIgnoreCase(User.AuthProvider.FACEBOOK.toString().toLowerCase())) {
return new FacebookOAuth2UserInfo(attributes);
} else {
throw new IllegalArgumentException("Sorry! Login with " + registrationId + " is not supported yet.");
}
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/persistence/BaseJpaAdapter.java
================================================
package com.tomo.mcauthentication.infrastructure.persistence;
import com.tomo.ddd.domain.BaseRepository;
import com.tomo.ddd.infrastructure.persistence.springdata.jpa.McCrudRepository;
import java.util.List;
import java.util.Optional;
//https://github.com/benthurley82/generic-type-resolver-test/blob/main/src/main/java/com/example/test/AbstractFoo.java
public class BaseJpaAdapter implements BaseRepository {
protected E jpaRepository;
public BaseJpaAdapter(E jpaRepository) {
this.jpaRepository = jpaRepository;
}
@Override
public T save(T entity) {
return (T) jpaRepository.save(entity);
}
@Override
public T findById(ID id) {
Optional entity = jpaRepository.findById(id);
return entity.isPresent() ? entity.get() : null;
}
@Override
public List findAll() {
return jpaRepository.findAll();
}
@Override
public void saveAll(List entities) {
jpaRepository.saveAll(entities);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/persistence/SessionJpaRepository.java
================================================
package com.tomo.mcauthentication.infrastructure.persistence;
import com.tomo.ddd.infrastructure.persistence.springdata.jpa.McCrudRepository;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionId;
import com.tomo.mcauthentication.domain.users.UserId;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface SessionJpaRepository extends McCrudRepository {
List findAllByUserId(UserId userId);
Session findSessionByAccessToken(String accessToken);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/persistence/SessionRepositoryJpaAdapter.java
================================================
package com.tomo.mcauthentication.infrastructure.persistence;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.SessionId;
import com.tomo.mcauthentication.domain.session.SessionRepository;
import com.tomo.mcauthentication.domain.users.UserId;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public class SessionRepositoryJpaAdapter extends BaseJpaAdapter implements SessionRepository {
public SessionRepositoryJpaAdapter(SessionJpaRepository jpaRepository) {
super(jpaRepository);
}
@Override
public SessionId nextIdentity() {
return new SessionId(UUID.randomUUID());
}
@Override
public List findByUserId(UserId userId) {
return jpaRepository.findAllByUserId(userId);
}
@Override
public Session findByAccessToken(String anAccessToken) {
return jpaRepository.findSessionByAccessToken(anAccessToken);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/persistence/UserJpaRepository.java
================================================
package com.tomo.mcauthentication.infrastructure.persistence;
import com.tomo.ddd.infrastructure.persistence.springdata.jpa.McCrudRepository;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
import org.springframework.stereotype.Repository;
@Repository
public interface UserJpaRepository extends McCrudRepository {
User findUserByEmail(String email);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/persistence/UserRegistrationJpaRepository.java
================================================
package com.tomo.mcauthentication.infrastructure.persistence;
import com.tomo.ddd.infrastructure.persistence.springdata.jpa.McCrudRepository;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.users.UserId;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
@Qualifier("UserRegistrationJpaRepository")
public interface UserRegistrationJpaRepository extends McCrudRepository {
long countByEmail(String email);
List findAllByEmailIn(List email);
UserRegistration findUserRegistrationByConfirmationCode(String confirmLink);
UserRegistration findUserRegistrationByEmail(String email);
UserRegistration findUserRegistrationByRecoveryCode(String recoveryCode);
UserRegistration findUserRegistrationByUserId(UserId userId);
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/persistence/UserRegistrationJpaRepositoryAdapter.java
================================================
package com.tomo.mcauthentication.infrastructure.persistence;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
import com.tomo.mcauthentication.domain.users.UserId;
import java.util.List;
public class UserRegistrationJpaRepositoryAdapter
extends BaseJpaAdapter
implements UserRegistrationRepository {
public UserRegistrationJpaRepositoryAdapter(UserRegistrationJpaRepository jpaRepository) {
super(jpaRepository);
}
@Override
public long countByEmailAndStatus(String email, UserRegistrationStatus status) {
return jpaRepository.countByEmail(email);
}
@Override
public List findAllByEmail(List emails) {
return jpaRepository.findAllByEmailIn(emails);
}
@Override
public UserRegistration findByEmail(String anEmail) {
return jpaRepository.findUserRegistrationByEmail(anEmail);
}
@Override public UserRegistration findByConfirmationCode(String confirmationCode) {
return jpaRepository.findUserRegistrationByConfirmationCode(confirmationCode);
}
@Override
public UserRegistration findByRecoveryCode(String aRecoveryCode) {
return jpaRepository.findUserRegistrationByRecoveryCode(aRecoveryCode);
}
@Override public UserRegistration findByUserId(UserId anUserId) {
return jpaRepository.findUserRegistrationByUserId(anUserId);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/persistence/UserRepositoryJpaAdapter.java
================================================
package com.tomo.mcauthentication.infrastructure.persistence;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
import com.tomo.mcauthentication.domain.users.UserRepository;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class UserRepositoryJpaAdapter extends BaseJpaAdapter implements UserRepository {
public UserRepositoryJpaAdapter(UserJpaRepository userJpaRepository) {
super(userJpaRepository);
}
@Override
public UserId nextIdentity() {
return new UserId(UUID.randomUUID());
}
@Override
public User findByEmail(String anEmail) {
return jpaRepository.findUserByEmail(anEmail);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/processing/ErrorCommandHandlerDecorator.java
================================================
package com.tomo.mcauthentication.infrastructure.processing;
import com.tomo.mcauthentication.application.configuration.AbstractVoidyCommandHandler;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.application.contracts.Command;
public class ErrorCommandHandlerDecorator extends AbstractVoidyCommandHandler {
CommandHandler commandHandler;
public ErrorCommandHandlerDecorator(CommandHandler commandHandler) {
this.commandHandler = commandHandler;
}
@Override
protected void abstractHandle(T aCommand) {
commandHandler.handle(aCommand);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/processing/LoggingCommandHandlerDecorator.java
================================================
package com.tomo.mcauthentication.infrastructure.processing;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.Response;
public class LoggingCommandHandlerDecorator implements CommandHandler {
CommandHandler commandHandler;
public LoggingCommandHandlerDecorator(CommandHandler commandHandler) {
this.commandHandler = commandHandler;
}
@Override
public Response handle(Command aCommand) {
//todo log
return commandHandler.handle(aCommand);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/processing/LoggingQueryHandlerDecorator.java
================================================
package com.tomo.mcauthentication.infrastructure.processing;
import com.tomo.mcauthentication.application.configuration.QueryHandler;
import com.tomo.mcauthentication.application.contracts.Query;
import com.tomo.mcauthentication.application.contracts.Response;
public class LoggingQueryHandlerDecorator implements QueryHandler {
QueryHandler queryHandler;
public LoggingQueryHandlerDecorator(QueryHandler queryHandler) {
this.queryHandler = queryHandler;
}
@Override
public Response handle(Query query) {
return queryHandler.handle(query);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/processing/PipelineBuilder.java
================================================
package com.tomo.mcauthentication.infrastructure.processing;
public interface PipelineBuilder {
PipelineBuilder with(C aRequest);
R build();
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/processing/builder/AbstractPipelineBuilder.java
================================================
package com.tomo.mcauthentication.infrastructure.processing.builder;
import com.tomo.mcauthentication.application.configuration.RequestHandler;
import com.tomo.mcauthentication.application.contracts.Request;
import com.tomo.mcauthentication.infrastructure.processing.PipelineBuilder;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public abstract class AbstractPipelineBuilder implements PipelineBuilder, ApplicationContextAware {
protected E request;
protected S handler;
protected ApplicationContext applicationContext;
@Override
public PipelineBuilder with(E aRequest) {
this.request = aRequest;
this.handler = this.getHandler();
return this;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
protected S getHandler() {
String beanName = this.getHandlerName();
return (S) applicationContext.getBean(beanName);
}
protected String getHandlerName() {
String fullHandlerName = this.request.getClass().getSimpleName() + "Handler";
return Character.toLowerCase(fullHandlerName.charAt(0)) + fullHandlerName.substring(1);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/processing/builder/CommandHandlerPipelineBuilder.java
================================================
package com.tomo.mcauthentication.infrastructure.processing.builder;
import com.tomo.mcauthentication.application.configuration.CommandHandler;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.infrastructure.processing.LoggingCommandHandlerDecorator;
import com.tomo.mcauthentication.infrastructure.processing.PipelineBuilder;
public class CommandHandlerPipelineBuilder extends AbstractPipelineBuilder {
public CommandHandlerPipelineBuilder() {}
@Override
public CommandHandlerPipelineBuilder with(Command aRequest) {
return (CommandHandlerPipelineBuilder) super.with(aRequest);
}
@Override
public CommandHandler build() {
return new LoggingCommandHandlerDecorator(handler);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/processing/builder/QueryHandlerPipelineBuilder.java
================================================
package com.tomo.mcauthentication.infrastructure.processing.builder;
import com.tomo.mcauthentication.application.configuration.QueryHandler;
import com.tomo.mcauthentication.application.contracts.Query;
import com.tomo.mcauthentication.infrastructure.processing.LoggingQueryHandlerDecorator;
import com.tomo.mcauthentication.infrastructure.processing.PipelineBuilder;
public class QueryHandlerPipelineBuilder extends AbstractPipelineBuilder {
public QueryHandlerPipelineBuilder() {
}
@Override
public QueryHandlerPipelineBuilder with(Query aRequest) {
return (QueryHandlerPipelineBuilder) super.with(aRequest);
}
@Override
public QueryHandler build() {
return new LoggingQueryHandlerDecorator(handler);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/service/MD5EncryptionService.java
================================================
package com.tomo.mcauthentication.infrastructure.service;
import com.tomo.ddd.AssertionConcern;
import com.tomo.mcauthentication.domain.EncryptionService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.math.BigInteger;
import java.security.MessageDigest;
@Service
@Qualifier("MD5EncryptionService")
public class MD5EncryptionService extends AssertionConcern implements EncryptionService {
public MD5EncryptionService() {
super();
}
@Override
public String encryptedValue(String aPlainTextValue) {
this.assertArgumentNotEmpty(
aPlainTextValue,
"Plain text value to encrypt must be provided.");
String encryptedValue = null;
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(aPlainTextValue.getBytes("UTF-8"));
BigInteger bigInt = new BigInteger(1, messageDigest.digest());
encryptedValue = bigInt.toString(16);
} catch (Exception e) {
throw new IllegalStateException(e);
}
return encryptedValue;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/configuration/AppConfig.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.configuration;
import com.tomo.mcauthentication.application.contracts.McAuthenticationModule;
import com.tomo.ddd.email.EmailSender;
import com.tomo.ddd.event.EventStore;
import com.tomo.ddd.infrastructure.persistence.springdata.jpa.EventStoreJpaRepositoryAdapter;
import com.tomo.ddd.infrastructure.persistence.springdata.jpa.StoredEventJpaRepository;
import com.tomo.ddd.port.adapter.message.email.MailGunMessageSender;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Service;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.users.UserRepository;
import com.tomo.mcauthentication.infrastructure.McAuthenticationModuleExecutor;
import com.tomo.mcauthentication.infrastructure.http.oauth2.FacebookOAuth2Authentication;
import com.tomo.mcauthentication.infrastructure.http.oauth2.GoogleOAuth2Authentication;
import com.tomo.mcauthentication.infrastructure.persistence.UserJpaRepository;
import com.tomo.mcauthentication.infrastructure.persistence.UserRegistrationJpaRepository;
import com.tomo.mcauthentication.infrastructure.persistence.UserRegistrationJpaRepositoryAdapter;
import com.tomo.mcauthentication.infrastructure.persistence.UserRepositoryJpaAdapter;
import com.tomo.mcauthentication.infrastructure.processing.builder.CommandHandlerPipelineBuilder;
import com.tomo.mcauthentication.infrastructure.processing.builder.QueryHandlerPipelineBuilder;
import com.tomo.mcauthentication.infrastructure.springboot.filter.TokenAuthenticationFilter;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@Configuration
@EnableJpaRepositories({
"com.tomo.ddd.infrastructure.persistence.springdata.jpa",
"com.tomo.mcauthentication.infrastructure.persistence"
})
public class AppConfig {
@Autowired
UserRegistrationJpaRepository userRegistrationJpaRepository;
@Autowired
UserJpaRepository userJpaRepository;
@Autowired
private Environment env;
@Autowired
AppProperties appProperties;
@Autowired
StoredEventJpaRepository storedEventJpaRepository;
@Bean
EventStore eventStore() {
return new EventStoreJpaRepositoryAdapter(storedEventJpaRepository);
}
@Bean
String recoveryLink() {
return appProperties.getGui().getRecoveryRoute();
}
@Bean
String confirmationLink() {
return appProperties.getGui().getConfirmationRoute();
}
@Bean
net.sargue.mailgun.Configuration mailGunConfiguration() {
MessageProperties.Email.MailGun mailGun = appProperties.getMessage().getEmail().getMailGun();
net.sargue.mailgun.Configuration configuration = new net.sargue.mailgun.Configuration()
.domain(mailGun.getDomains())
.apiUrl(mailGun.getApiUrl())
.apiKey(mailGun.getApiKey())
.from(mailGun.getFrom().getName(), mailGun.getFrom().getEmail());
return configuration;
}
@Bean
EmailSender emailMessageSender(net.sargue.mailgun.Configuration mailGunConfiguration) {
return new MailGunMessageSender(mailGunConfiguration);
}
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
@Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
@Bean
McAuthenticationModule authenticationModule(CommandHandlerPipelineBuilder commandHandlerPipelineBuilder,
QueryHandlerPipelineBuilder queryHandlerPipelineBuilder) {
return new McAuthenticationModuleExecutor(commandHandlerPipelineBuilder, queryHandlerPipelineBuilder);
}
@Bean
CommandHandlerPipelineBuilder commandHandlerPipelineBuilder() {
return new CommandHandlerPipelineBuilder();
}
@Bean
QueryHandlerPipelineBuilder queryHandlerPipelineBuilder() {
return new QueryHandlerPipelineBuilder();
}
@Bean
UserRegistrationRepository userRegistrationRepository(){
return new UserRegistrationJpaRepositoryAdapter(userRegistrationJpaRepository);
}
@Bean
UserRepository userRepository() {
return new UserRepositoryJpaAdapter(userJpaRepository);
}
@Bean
@Qualifier("facebookClientRegistration")
ClientRegistration facebookClientRegistration() {
String clientRootProperty = "spring.security.oauth2.client.registration.facebook";
String clientId = env.getProperty(clientRootProperty + ".client-id");
String clientSecret = env.getProperty(clientRootProperty + ".client-secret");
return CommonOAuth2Provider.FACEBOOK.getBuilder("facebook")
.clientId(clientId).clientSecret(clientSecret).build();
}
@Bean
@Qualifier("googleClientRegistration")
ClientRegistration googleClientRegistration() {
String clientRootProperty = "spring.security.oauth2.client.registration.facebook";
String clientId = env.getProperty(clientRootProperty + ".client-id");
String clientSecret = env.getProperty(clientRootProperty + ".client-secret");
return CommonOAuth2Provider.FACEBOOK.getBuilder("facebook")
.clientId(clientId).clientSecret(clientSecret).build();
}
@Bean
@Qualifier("facebookOAuth2Service")
OAuth2Service facebookOAuth2Service(
FacebookOAuth2Authentication facebookOAuth2Authentication,
UserRepository userRepository) {
return new OAuth2Service(facebookOAuth2Authentication, userRepository);
}
@Bean
@Qualifier("googleOAuth2Service")
OAuth2Service googleOAuth2Service(
GoogleOAuth2Authentication googleOAuth2Authentication,
UserRepository userRepository) {
return new OAuth2Service(googleOAuth2Authentication, userRepository);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/configuration/AppProperties.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private final Auth auth = new Auth();
private final OAuth2 oauth2 = new OAuth2();
private final MessageProperties message = new MessageProperties();
private String baseUrl;
private final GUIProperties gui = new GUIProperties();
public static class Auth {
private String tokenSecret;
private String sessionAuthTokenName;
private long tokenExpirationMsec;
public String getTokenSecret() {
return tokenSecret;
}
public void setTokenSecret(String tokenSecret) {
this.tokenSecret = tokenSecret;
}
public long getTokenExpirationMsec() {
return tokenExpirationMsec;
}
public void setTokenExpirationMsec(long tokenExpirationMsec) {
this.tokenExpirationMsec = tokenExpirationMsec;
}
public String getSessionAuthTokenName() {
return sessionAuthTokenName;
}
public void setSessionAuthTokenName(String sessionAuthTokenName) {
this.sessionAuthTokenName = sessionAuthTokenName;
}
}
public static final class OAuth2 {
private List authorizedRedirectUris = new ArrayList<>();
public List getAuthorizedRedirectUris() {
return authorizedRedirectUris;
}
public OAuth2 authorizedRedirectUris(List authorizedRedirectUris) {
this.authorizedRedirectUris = authorizedRedirectUris;
return this;
}
}
public Auth getAuth() {
return auth;
}
public OAuth2 getOauth2() {
return oauth2;
}
public MessageProperties getMessage() {
return message;
}
public GUIProperties getGui() {
return gui;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/configuration/GUIProperties.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.configuration;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class GUIProperties {
String baseUrl;
String recoveryRoute;
String confirmationRoute;
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/configuration/MessageProperties.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.configuration;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class MessageProperties {
private Email email = new Email();
@Getter
@Setter
@NoArgsConstructor
public static class Email {
MailGun mailGun = new MailGun();
String fakeEmail = "tomo.landeka02@gmail.com";
@Getter
@Setter
@NoArgsConstructor
public static class MailGun {
String domains;
String apiUrl;
String apiKey;
From from;
@Getter
@Setter
@NoArgsConstructor
public static class From {
String name;
String email;
}
}
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/configuration/SecurityConfig.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.configuration;
import com.tomo.mcauthentication.infrastructure.springboot.security.OAuth2AuthenticationFailureHandler;
import com.tomo.mcauthentication.infrastructure.springboot.security.OAuth2AuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
@Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.and()
.oauth2Login()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/configuration/SwaggerConfig.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/configuration/SwaggerUiWebMvcConfigurer.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class SwaggerUiWebMvcConfigurer implements WebMvcConfigurer {
private final String baseUrl;
public SwaggerUiWebMvcConfigurer(@Value("${app.base-url:}") String baseUrl) {
this.baseUrl = baseUrl;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String baseUrl = StringUtils.trimTrailingCharacter(this.baseUrl, '/');
registry.
addResourceHandler(baseUrl + "/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.resourceChain(false);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController(baseUrl + "/swagger-ui/")
.setViewName("forward:" + baseUrl + "/swagger-ui/index.html");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/api/pet")
.allowedOrigins("http://editor.swagger.io");
registry
.addMapping("/v2/api-docs.*")
.allowedOrigins("http://editor.swagger.io");
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/controller/AbstractController.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.controller;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.application.contracts.McAuthenticationModule;
import com.tomo.mcauthentication.application.contracts.Query;
import com.tomo.mcauthentication.application.contracts.Request;
import com.tomo.mcauthentication.application.contracts.Response;
import com.tomo.mcauthentication.application.contracts.security.Authenticate;
import com.tomo.mcauthentication.domain.session.TokenProvider;
import com.tomo.mcauthentication.infrastructure.springboot.configuration.AppProperties;
import com.tomo.mcauthentication.infrastructure.util.CookieUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
public abstract class AbstractController {
@Autowired
protected McAuthenticationModule authenticationModule;
@Autowired
protected AppProperties properties;
@Autowired
protected HttpServletRequest request;
@Autowired
protected TokenProvider tokenProvider;
protected Response executeCommand(Command command) {
this.setAuthToken(command);
return authenticationModule.executeCommand(command);
}
protected T executeCommand(Command command, Class tclass) {
this.setAuthToken(command);
return tclass.cast(authenticationModule.executeCommand(command));
}
protected T executeQuery(Query query, Class tclass) {
this.setAuthToken(query);
return tclass.cast(authenticationModule.executeQuery(query));
}
private void setAuthToken(Request request) {
if (request instanceof Authenticate) {
String jwt = getJwtFromRequest();
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
((Authenticate) request).setAuthToken(jwt);
}
}
}
private String getJwtFromRequest() {
return CookieUtils.getCookie(request, properties.getAuth().getSessionAuthTokenName())
.map(cookie -> CookieUtils.deserialize(cookie, String.class))
// .filter(cookie -> StringUtils.hasText(cookie) && cookie.startsWith("Bearer "))
// .map(cookie -> cookie.substring(7))
.orElse(null);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/controller/AuthenticationController.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.controller;
import com.tomo.mcauthentication.application.authentication.command.EmailLoginCommand;
import com.tomo.mcauthentication.application.authentication.command.FacebookLoginCommand;
import com.tomo.mcauthentication.application.authentication.command.GoogleLoginCommand;
import com.tomo.mcauthentication.application.authentication.command.LogoutCommand;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.contracts.Command;
import com.tomo.mcauthentication.infrastructure.springboot.controller.RestApiRoutes.AuthRoutes;
import com.tomo.mcauthentication.infrastructure.springboot.security.CurrentUser;
import com.tomo.mcauthentication.infrastructure.springboot.security.UserAuthPrincipal;
import com.tomo.mcauthentication.infrastructure.util.CookieUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping(path = "/")
public class AuthenticationController extends AbstractController {
@Autowired
private HttpServletResponse response;
@RequestMapping(method = RequestMethod.POST, path = AuthRoutes.FORM_LOGIN)
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity formLogin(@CurrentUser UserAuthPrincipal user,
@RequestBody @Validated EmailLoginCommand command) {
return ResponseEntity.ok(this.executeCommand(command, user));
}
@RequestMapping(method = RequestMethod.POST, path = AuthRoutes.FACEBOOK_LOGIN)
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity facebookLogin(@CurrentUser UserAuthPrincipal user,
@RequestBody @Validated FacebookLoginCommand command){
return ResponseEntity.ok(this.executeCommand(command, user));
}
@RequestMapping(method = RequestMethod.POST, path = AuthRoutes.GOOGLE_LOGIN)
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity googleLogin(@CurrentUser UserAuthPrincipal user,
@RequestBody @Validated GoogleLoginCommand command) {
return ResponseEntity.ok(this.executeCommand(command, user));
}
@RequestMapping(method = RequestMethod.PATCH, path = AuthRoutes.LOGOUT)
public ResponseEntity logout(@CurrentUser UserAuthPrincipal user,
@RequestBody @Validated LogoutCommand command) {
if (user == null) {
return ResponseEntity.badRequest().body("Missing session cookie.");
}
command.setAuthToken(user.getSession().getAccessToken());
authenticationModule.executeCommand(command);
return ResponseEntity.ok().build();
}
protected SessionDto executeCommand(Command command, UserAuthPrincipal user) {
if (user != null) {
return user.getSession();
}
SessionDto session = super.executeCommand(command, SessionDto.class);
addAccessTokenToCookie(session.getAccessToken());
return session;
}
private void addAccessTokenToCookie(String accessToken) {
CookieUtils.addCookie(
response,
properties.getAuth().getSessionAuthTokenName(),
CookieUtils.serialize(accessToken),
(int) TimeUnit.MILLISECONDS.toSeconds(properties.getAuth().getTokenExpirationMsec()));
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/controller/RegistrationController.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.controller;
import com.tomo.mcauthentication.application.recovery.command.CreatePasswordRecoveryCodeCommand;
import com.tomo.mcauthentication.application.recovery.command.UpdatePasswordWithRecoveryCodeCommand;
import com.tomo.mcauthentication.application.registration.command.ConfirmUserRegistrationCommand;
import com.tomo.mcauthentication.application.registration.command.RegisterNewUserCommand;
import com.tomo.mcauthentication.infrastructure.springboot.controller.RestApiRoutes.RegistrationRoutes;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(path = "/")
public class RegistrationController extends AbstractController {
@RequestMapping(method = RequestMethod.POST, path = RegistrationRoutes.FORM_REGISTRATION)
@ResponseStatus(HttpStatus.CREATED)
public void formRegister(@RequestBody @Validated RegisterNewUserCommand command){
this.executeCommand(command);
}
@RequestMapping(method = RequestMethod.POST, path = RegistrationRoutes.CONFIRM_REGISTRATION)
@ResponseStatus(HttpStatus.OK)
public void formRegisterConfirmation(@RequestParam String confirmationCode){
this.executeCommand(new ConfirmUserRegistrationCommand(confirmationCode));
}
@RequestMapping(method = RequestMethod.PATCH, path = RegistrationRoutes.CREATE_PASSWORD_RECOVERY_CODE)
@ResponseStatus(HttpStatus.OK)
public void formRegisterRecovery(@RequestBody @Validated CreatePasswordRecoveryCodeCommand command){
this.executeCommand(command);
}
@RequestMapping(method = RequestMethod.PATCH, path = RegistrationRoutes.PASSWORD_RESET)
@ResponseStatus(HttpStatus.OK)
public void passwordReset(@RequestBody @Validated UpdatePasswordWithRecoveryCodeCommand command) {
this.executeCommand(command);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/controller/RestApiRoutes.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.controller;
public class RestApiRoutes {
public static final String REST = "/rest";
public static final String PUBLIC = REST + "/public";
public static final String PRIVATE = REST + "/private";
public static class RegistrationRoutes {
public static final String FORM_REGISTRATION = PUBLIC + "/register/form";
public static final String CONFIRM_REGISTRATION = PUBLIC + "/register/confirm/";
public static final String CREATE_PASSWORD_RECOVERY_CODE = PUBLIC + "/register/password/recovery-code";
public static final String PASSWORD_RESET = PUBLIC + "/register/password/reset";
}
public static class AuthRoutes {
public static final String FORM_LOGIN = PUBLIC + "/login/form";
public static final String FACEBOOK_LOGIN = PUBLIC + "/login/facebook";
public static final String GOOGLE_LOGIN = PUBLIC + "/login/google";
public static final String LOGOUT = PRIVATE + "/logout";
}
public static class User {
public static final String USER = PRIVATE + "/user";
public static final String USER_DETAILS = USER + "/{userId}";
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/controller/UserController.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.controller;
import com.tomo.mcauthentication.application.users.command.ChangeUserDetailsCommand;
import com.tomo.mcauthentication.application.users.dto.BaseUserDto;
import com.tomo.mcauthentication.application.users.query.GetUserQuery;
import com.tomo.mcauthentication.infrastructure.springboot.controller.RestApiRoutes.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
@RequestMapping(path = "/")
public class UserController extends AbstractController {
@RequestMapping(method = RequestMethod.GET, path = User.USER_DETAILS)
public ResponseEntity user(@PathVariable(value = "userId") String userId){
BaseUserDto dto = this.executeQuery(new GetUserQuery(userId), BaseUserDto.class);
return ResponseEntity.ok(dto);
}
@RequestMapping(method = RequestMethod.PATCH, path = User.USER_DETAILS)
public ResponseEntity user(
@PathVariable(value = "userId") UUID userId,
@RequestBody @Validated ChangeUserDetailsCommand command){
command.setUserId(userId);
this.executeCommand(command);
return new ResponseEntity(HttpStatus.OK);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/filter/TokenAuthenticationFilter.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.filter;
import com.tomo.mcauthentication.application.authentication.command.SessionAuthenticationCommand;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.contracts.McAuthenticationModule;
import com.tomo.mcauthentication.domain.session.TokenProvider;
import com.tomo.mcauthentication.domain.users.UserId;
import com.tomo.mcauthentication.domain.users.UserRepository;
import com.tomo.mcauthentication.infrastructure.springboot.configuration.AppProperties;
import com.tomo.mcauthentication.infrastructure.springboot.security.UserAuthPrincipal;
import com.tomo.mcauthentication.infrastructure.springboot.security.UserAuthToken;
import com.tomo.mcauthentication.infrastructure.util.CookieUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class TokenAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private TokenProvider tokenProvider;
@Autowired
McAuthenticationModule mcAuthenticationModuleExecutor;
@Autowired
protected AppProperties properties;
private static final Logger logger = LoggerFactory.getLogger(TokenAuthenticationFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
//maybe set authentication, UserId, SessionId, AccessToken
//But only if the intention is to use hasRole, isAuthenticate on RestController
SessionDto sessionDetails = (SessionDto) mcAuthenticationModuleExecutor.executeCommand(new SessionAuthenticationCommand(jwt));
jwt = sessionDetails.getAccessToken();
if (!sessionDetails.getAccessToken().equals(jwt)) {
CookieUtils.updateCookie(
request,
response,
properties.getAuth().getSessionAuthTokenName(),
CookieUtils.serialize(sessionDetails.getAccessToken()),
(int) TimeUnit.MILLISECONDS.toSeconds(properties.getAuth().getTokenExpirationMsec()));
}
setAuthentication(sessionDetails);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
SecurityContextHolder.getContext().setAuthentication(null);
}
filterChain.doFilter(request, response);
}
private void setAuthentication(SessionDto sessionDetails) {
UserAuthToken currentAuthentication = null;
try {
currentAuthentication = (UserAuthToken) SecurityContextHolder.getContext().getAuthentication();
} catch (Exception e) {
currentAuthentication = null;
}
UserAuthToken newAuthentication = null;
if (currentAuthentication == null) {
newAuthentication = new UserAuthToken(new UserAuthPrincipal(sessionDetails));
} else {
UserAuthPrincipal principal = (UserAuthPrincipal) currentAuthentication.getPrincipal();
if (!principal.getSession().getAccessToken().equals(sessionDetails.getAccessToken())) {
newAuthentication = new UserAuthToken(new UserAuthPrincipal(sessionDetails));
}
}
if (newAuthentication != null) {
SecurityContextHolder.getContext().setAuthentication(newAuthentication);
}
}
private String getJwtFromRequest(HttpServletRequest request) {
return CookieUtils.getCookie(request, properties.getAuth().getSessionAuthTokenName())
.map(cookie -> CookieUtils.deserialize(cookie, String.class))
// .filter(cookie -> StringUtils.hasText(cookie) && cookie.startsWith("Bearer "))
// .map(cookie -> cookie.substring(7))
.orElse(null);
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/security/CurrentUser.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.security;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/security/OAuth2AuthenticationFailureHandler.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.security;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class OAuth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
int a = 1;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/security/OAuth2AuthenticationSuccessHandler.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.util.Optional;
@Component
public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String targetUrl = determineTargetUrl(request, response, authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to redirect to " + targetUrl);
return;
}
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/security/UserAuthPrincipal.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.security;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.domain.users.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
public class UserAuthPrincipal implements UserDetails {
private SessionDto session;
private final Collection authorities;
private boolean enabled = true;
public UserAuthPrincipal(SessionDto session) {
this.session = session;
this.authorities = new ArrayList<>();
}
public SessionDto getSession() {
return session;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return new ArrayList<>();
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return session.getUserId();
}
@Override
public boolean isAccountNonExpired() {
return this.enabled;
}
@Override
public boolean isAccountNonLocked() {
return this.enabled;
}
@Override
public boolean isCredentialsNonExpired() {
return this.enabled;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/springboot/security/UserAuthToken.java
================================================
package com.tomo.mcauthentication.infrastructure.springboot.security;
import com.tomo.mcauthentication.domain.users.User;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.Collection;
public class UserAuthToken extends AbstractAuthenticationToken {
private UserAuthPrincipal userPrincipal;
public UserAuthToken(UserAuthPrincipal userPrincipal) {
super(userPrincipal.getAuthorities());
super.setAuthenticated(true);
this.userPrincipal = userPrincipal;
}
public UserAuthToken(String authToken, Collection extends GrantedAuthority> authorities) {
super(authorities);
this.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return userPrincipal;
}
}
================================================
FILE: src/main/java/com/tomo/mcauthentication/infrastructure/util/CookieUtils.java
================================================
package com.tomo.mcauthentication.infrastructure.util;
import org.springframework.util.SerializationUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
import java.util.Optional;
public class CookieUtils {
public static Optional getCookie(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
return Optional.of(cookie);
}
}
}
return Optional.empty();
}
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
Cookie cookie = new Cookie(name, value);
cookie.setPath("/");
cookie.setMaxAge(5000);
response.addCookie(cookie);
}
public static void updateCookie(HttpServletRequest request, HttpServletResponse response, String name, String value, int maxAge) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie: cookies) {
if (cookie.getName().equals(name)) {
cookie.setValue(value);
cookie.setPath("/");
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
}
}
}
public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String name) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie: cookies) {
if (cookie.getName().equals(name)) {
cookie.setValue("");
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
}
}
public static String serialize(Object object) {
return Base64.getUrlEncoder()
.encodeToString(SerializationUtils.serialize(object));
}
public static T deserialize(Cookie cookie, Class cls) {
return cls.cast(SerializationUtils.deserialize(
Base64.getUrlDecoder().decode(cookie.getValue())));
}
}
================================================
FILE: src/main/resources/application.yml
================================================
server:
port: 8080
debug: true
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mc_authentication
username: mcuser
password: mcuser
driver-class-name: org.postgresql.Driver
jpa:
show-sql: true
hibernate:
ddl-auto: none
naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQL9Dialect
flyway:
enabled: true
baselineOnMigrate: true
schemas: public
security:
oauth2:
client:
registration:
google:
clientId: your-client-id.apps.googleusercontent.com
clientSecret: your-client-secret
scope:
- email
- profile
facebook:
clientId: your-client-id
clientSecret: your-client-secret
scope:
- email
- public_profile
provider:
facebook:
authorizationUri: https://www.facebook.com/v3.0/dialog/oauth
tokenUri: https://graph.facebook.com/v3.0/oauth/access_token
userInfoUri: https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture.width(250).height(250)
app:
base-url: http://localhost:8080
auth:
tokenSecret: 04ca023b39512e46d0c2cf4b48d5aac61d34302994c87ed4eff225dcf3b0a218739f3897051a057f9b846a69ea2927a587044164b7bae5e1306219d50b588cb1
tokenExpirationMsec: 864000000
sessionAuthTokenName: "dei-www"
cors:
allowedOrigins: http://localhost:3000,http://localhost:8080
message:
email:
mailgun:
domains: "[your-sandbox].mailgun.org"
api-url: "https://api.mailgun.net/v3/"
api-key: "[mailgun-api-key]"
from:
name: "mc team"
email: "tomo.landeka02@gmail.com"
gui:
base-url: "localhost:3000"
recovery-route: "${app.gui.base-url}/reset-password/?recoveryCode="
confirmation-route: "${app.gui.base-url}/register/confirm/?confirmationCode="
================================================
FILE: src/main/resources/db/migration/V2022_02_02_1124__initial_structure.sql
================================================
CREATE TABLE mcuser (
id UUID PRIMARY KEY NOT NULL,
first_name VARCHAR(255),
last_name VARCHAR(255),
provider VARCHAR(50) NOT NULL,
email VARCHAR(255) NOT NULL
CONSTRAINT mcuser_email_unique unique,
concurrency_version BIGINT,
created TIMESTAMP NOT NULL DEFAULT Now(),
modified TIMESTAMP
);
CREATE TABLE user_registration (
id BIGSERIAL PRIMARY KEY,
first_name VARCHAR(255),
last_name VARCHAR(255),
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
confirmation_code VARCHAR(255) NOT NULL,
status VARCHAR(255) NOT NULL,
register_date TIMESTAMP,
recovery_code VARCHAR(255),
recovery_code_expiration_date TIMESTAMP,
user_id UUID
CONSTRAINT user_registration_mcuser_id_fk REFERENCES mcuser,
concurrency_version BIGINT,
created TIMESTAMP DEFAULT now() NOT NULL,
modified TIMESTAMP
);
create table session (
id UUID PRIMARY KEY,
user_id UUID NOT NULL CONSTRAINT session_mcuser_id_fk REFERENCES mcuser,
access_token TEXT NOT NULL,
expiration_date TIMESTAMP,
token_type TEXT NOT NULL,
refresh_token TEXT,
user_agent TEXT,
ip_address VARCHAR(45),
last_activity TIMESTAMP,
created TIMESTAMP DEFAULT now() NOT NULL,
modified TIMESTAMP
);
CREATE UNIQUE INDEX user_registration_user_id_uindex ON user_registration(user_id);
CREATE TABLE stored_event (
event_id BIGSERIAL PRIMARY KEY,
event_body TEXT NOT NULL,
occurred_on TIMESTAMP NOT NULL,
type_name VARCHAR (200) NOT NULL
);
================================================
FILE: src/main/resources/repo/org/tomo/ddd_common/1.0.0/ddd_common-1.0.0.jar.md5
================================================
d6f732badc879e635745ef1ee13673d3
================================================
FILE: src/main/resources/repo/org/tomo/ddd_common/1.0.0/ddd_common-1.0.0.jar.sha1
================================================
49221e5069bd616ba23339076cad6478d3762710
================================================
FILE: src/main/resources/repo/org/tomo/ddd_common/1.0.0/ddd_common-1.0.0.pom
================================================
4.0.0
org.tomo
ddd_common
1.0.0
================================================
FILE: src/main/resources/repo/org/tomo/ddd_common/1.0.0/ddd_common-1.0.0.pom.md5
================================================
5238d5a53bcbfbe70a6cb87ed493d6f3
================================================
FILE: src/main/resources/repo/org/tomo/ddd_common/1.0.0/ddd_common-1.0.0.pom.sha1
================================================
423681301f4b73b38060c3c069f0141c0b54c660
================================================
FILE: src/main/resources/repo/org/tomo/ddd_common/maven-metadata.xml
================================================
org.tomo
ddd_common
1.0.0
1.0.0
20221113150911
================================================
FILE: src/main/resources/repo/org/tomo/ddd_common/maven-metadata.xml.md5
================================================
2dfb0c4eb83fb0e21037e00e71ab51cb
================================================
FILE: src/main/resources/repo/org/tomo/ddd_common/maven-metadata.xml.sha1
================================================
b984353efb5206ed31336cb8a1508c6453122adb
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/BaseIntegrationTest.java
================================================
package com.tomo.mcauthentication.integration;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class BaseIntegrationTest { }
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/AbstractApplicationServiceTest.java
================================================
package com.tomo.mcauthentication.integration.application;
import com.tomo.ddd.email.EmailSender;
import com.tomo.mcauthentication.application.authentication.EmailLoginCommandHandler;
import com.tomo.mcauthentication.application.authentication.FacebookLoginCommandHandler;
import com.tomo.mcauthentication.application.authentication.GoogleLoginCommandHandler;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.recovery.SendPasswordRecoveryEmailCommandHandler;
import com.tomo.mcauthentication.application.registration.ConfirmUserRegistrationCommandHandler;
import com.tomo.mcauthentication.application.registration.RegisterNewUserCommandHandler;
import com.tomo.mcauthentication.application.registration.SendRegistrationConfirmationEmailCommandHandler;
import com.tomo.mcauthentication.application.registration.command.ConfirmUserRegistrationCommand;
import com.tomo.mcauthentication.domain.oauth2.OAuth2Principal;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserRepository;
import com.tomo.mcauthentication.infrastructure.http.oauth2.FacebookOAuth2Authentication;
import com.tomo.mcauthentication.infrastructure.http.oauth2.GoogleOAuth2Authentication;
import com.tomo.mcauthentication.integration.BaseIntegrationTest;
import com.tomo.mcauthentication.testdata.CommandObjectMother;
import com.tomo.mcauthentication.testdata.StaticFields;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import java.util.Arrays;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
public abstract class AbstractApplicationServiceTest extends BaseIntegrationTest {
@Autowired
protected FacebookLoginCommandHandler facebookLoginCommandHandler;
@Autowired
protected GoogleLoginCommandHandler googleLoginCommandHandler;
@Autowired
protected EmailLoginCommandHandler emailLoginCommandHandler;
@MockBean
protected FacebookOAuth2Authentication facebookOAuth2Authentication;
@MockBean
protected GoogleOAuth2Authentication googleOAuth2Authentication;
@Autowired
protected ConfirmUserRegistrationCommandHandler confirmUserRegistrationCommandHandler;
@Autowired
protected RegisterNewUserCommandHandler registerNewUserCommandHandler;
@Autowired
protected UserRegistrationRepository userRegistrationRepository;
@Autowired
protected UserRepository userRepository;
@SpyBean
protected SendRegistrationConfirmationEmailCommandHandler sendRegistrationConfirmationEmailCommandHandler;
@SpyBean
protected SendPasswordRecoveryEmailCommandHandler sendPasswordRecoveryEmailCommandHandler;
@MockBean
protected EmailSender emailMessageSender;
protected User createFormUser() {
confirmUserRegistrationCommandHandler.handle(
new ConfirmUserRegistrationCommand(
createUserRegistration().getConfirmationCode()
));
return userRepository.findByEmail(StaticFields.USER_EMAIL);
}
protected UserRegistration createUserRegistration() {
registerNewUserCommandHandler.handle(CommandObjectMother.registerNewUserCommand());
Optional userRegistration = userRegistrationRepository
.findAllByEmail(Arrays.asList(StaticFields.USER_EMAIL))
.stream()
.findFirst();
return userRegistration.get();
}
protected SessionDto formLogin(){
createFormUser();
return emailLoginCommandHandler.handle(CommandObjectMother.emailLoginCommand());
}
protected User createFacbookUser() {
when(facebookOAuth2Authentication.authenticate(anyString()))
.thenReturn(oAuth2Principal(User.AuthProvider.FACEBOOK.toString()));
facebookLoginCommandHandler.handle(CommandObjectMother.facebookLoginCommand());
return userRepository.findByEmail(StaticFields.USER_EMAIL);
}
protected User createGoogleUser() {
when(googleOAuth2Authentication.authenticate(anyString()))
.thenReturn(oAuth2Principal(User.AuthProvider.GOOGLE.toString()));
googleLoginCommandHandler.handle(CommandObjectMother.googleLoginCommand());
return userRepository.findByEmail(StaticFields.USER_EMAIL);
}
private OAuth2Principal oAuth2Principal(String anProvider) {
return new OAuth2Principal(
StaticFields.USER_OAUTH_ID,
StaticFields.USER_EMAIL,
StaticFields.USER_FIRST_NAME,
StaticFields.USER_LAST_NAME,
"img",
anProvider);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/authentication/EmailLoginCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.authentication;
import com.tomo.ddd.domain.BusinessRuleValidationException;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import org.junit.Test;
import javax.transaction.Transactional;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
public class EmailLoginCommandHandlerTest extends AbstractApplicationServiceTest {
@Test
@Transactional
public void testUserFormLogin() {
assertNotNull(formLogin());
}
@Test
@Transactional
public void testNewFormUserFailedWhenGoogleUserExists() {
createGoogleUser();
assertThrows(BusinessRuleValidationException.class, this::createFormUser);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/authentication/FacebookLoginCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.authentication;
import com.tomo.ddd.domain.BusinessRuleValidationException;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import org.junit.Test;
import javax.transaction.Transactional;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
public class FacebookLoginCommandHandlerTest extends AbstractApplicationServiceTest {
@Test
@Transactional
public void testNewFacebookUserCreated() {
assertNotNull(createFacbookUser());
}
@Test
@Transactional
public void testNewFacebookUserFailedWhenGoogleUserExists() {
createGoogleUser();
assertThrows(BusinessRuleValidationException.class, this::createFacbookUser);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/authentication/GoogleLoginCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.authentication;
import com.tomo.ddd.domain.BusinessRuleValidationException;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import org.junit.Test;
import javax.transaction.Transactional;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
public class GoogleLoginCommandHandlerTest extends AbstractApplicationServiceTest {
@Test
@Transactional
public void testNewGoogleUserCreated() {
assertNotNull(createGoogleUser());
}
@Test
@Transactional
public void testNewGoogleUserFailedWhenFacebookUserExists() {
createFacbookUser();
assertThrows(BusinessRuleValidationException.class, this::createGoogleUser);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/authentication/LogoutCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.authentication;
import com.tomo.ddd.domain.BusinessRuleValidationException;
import com.tomo.mcauthentication.application.authentication.LogoutCommandHandler;
import com.tomo.mcauthentication.application.authentication.command.LogoutCommand;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.domain.session.SessionAuthenticationService;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertThrows;
public class LogoutCommandHandlerTest extends AbstractApplicationServiceTest {
@Autowired
LogoutCommandHandler logoutCommandHandler;
@Autowired
SessionAuthenticationService sessionAuthenticationService;
@Test
@Transactional
public void testLogout() {
SessionDto sessionDto = formLogin();
logoutCommandHandler.handle(new LogoutCommand(sessionDto.getAccessToken()));
assertThrows(BusinessRuleValidationException.class, () -> sessionAuthenticationService.authenticate(sessionDto.getAccessToken()));
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/authentication/SessionAuthenticationCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.authentication;
import com.tomo.mcauthentication.application.authentication.SessionAuthenticationCommandHandler;
import com.tomo.mcauthentication.application.authentication.command.SessionAuthenticationCommand;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.domain.session.SessionAuthenticationService;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertEquals;
public class SessionAuthenticationCommandHandlerTest extends AbstractApplicationServiceTest {
@Autowired
SessionAuthenticationCommandHandler commandHandler;
@Autowired
SessionAuthenticationService sessionAuthenticationService;
@Test
@Transactional
public void testAuthenticateSession() {
SessionDto initialSessionDto = formLogin();
SessionDto sessionDto = commandHandler.handle(new SessionAuthenticationCommand(initialSessionDto.getAccessToken()));
assertEquals(initialSessionDto.getAccessToken(), sessionDto.getAccessToken());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/recovery/CreatePasswordRecoveryCodeCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.recovery;
import com.tomo.mcauthentication.application.authentication.dto.RecoveryPasswordDto;
import com.tomo.mcauthentication.application.recovery.CreatePasswordRecoveryCodeCommandHandler;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import com.tomo.mcauthentication.testdata.CommandObjectMother;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
public class CreatePasswordRecoveryCodeCommandHandlerTest extends AbstractApplicationServiceTest {
@Autowired
CreatePasswordRecoveryCodeCommandHandler commandHandler;
@Test
@Transactional
public void testRecoveryCodeCreated() {
formLogin();
RecoveryPasswordDto recoveryPasswordDto = commandHandler.handle(CommandObjectMother.createPasswordRecoveryCodeCommand());
assertNotNull(recoveryPasswordDto);
verify(this.sendPasswordRecoveryEmailCommandHandler, Mockito.times(1)).handle(any());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/recovery/GetUserRegistrationWithRecoveryCodeQueryHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.recovery;
import com.tomo.mcauthentication.application.authentication.dto.RecoveryPasswordDto;
import com.tomo.mcauthentication.application.recovery.CreatePasswordRecoveryCodeCommandHandler;
import com.tomo.mcauthentication.application.recovery.GetUserRegistrationWithRecoveryCodeQueryHandler;
import com.tomo.mcauthentication.application.recovery.dto.GetUserRegistrationWithRecoveryCodeQuery;
import com.tomo.mcauthentication.application.registration.dto.UserRegistrationDto;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import com.tomo.mcauthentication.testdata.CommandObjectMother;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertNotNull;
public class GetUserRegistrationWithRecoveryCodeQueryHandlerTest extends AbstractApplicationServiceTest {
@Autowired
GetUserRegistrationWithRecoveryCodeQueryHandler queryHandler;
@Autowired
CreatePasswordRecoveryCodeCommandHandler commandHandler;
@Test
@Transactional
public void testGetUserRegistrationWithRecoveryCode() {
formLogin();
RecoveryPasswordDto recoveryPasswordDto = commandHandler.handle(CommandObjectMother.createPasswordRecoveryCodeCommand());
UserRegistrationDto userRegistrationDto = queryHandler.handle(new GetUserRegistrationWithRecoveryCodeQuery(recoveryPasswordDto.getRecoveryCode()));
assertNotNull(userRegistrationDto.getEmail());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/recovery/UpdatePasswordWithRecoveryCodeCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.recovery;
import com.tomo.mcauthentication.application.authentication.command.EmailLoginCommand;
import com.tomo.mcauthentication.application.authentication.dto.RecoveryPasswordDto;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.contracts.Voidy;
import com.tomo.mcauthentication.application.recovery.CreatePasswordRecoveryCodeCommandHandler;
import com.tomo.mcauthentication.application.recovery.UpdatePasswordWithRecoveryCodeCommandHandler;
import com.tomo.mcauthentication.application.recovery.command.UpdatePasswordWithRecoveryCodeCommand;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import com.tomo.mcauthentication.testdata.CommandObjectMother;
import com.tomo.mcauthentication.testdata.StaticFields;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertEquals;
public class UpdatePasswordWithRecoveryCodeCommandHandlerTest extends AbstractApplicationServiceTest {
@Autowired
CreatePasswordRecoveryCodeCommandHandler createPasswordRecoveryCodeCommandHandler;
@Autowired
UpdatePasswordWithRecoveryCodeCommandHandler commandHandler;
@Test
@Transactional
public void testUpdatePasswordWithRecoveryCode() {
formLogin();
RecoveryPasswordDto recoveryPasswordDto = createPasswordRecoveryCodeCommandHandler.handle(CommandObjectMother.createPasswordRecoveryCodeCommand());
assertEquals(commandHandler.handle(new UpdatePasswordWithRecoveryCodeCommand(
StaticFields.NEW_PASS,
StaticFields.NEW_PASS,
recoveryPasswordDto.getRecoveryCode())).getClass(),
Voidy.class);
assertEquals(
emailLoginCommandHandler.handle(new EmailLoginCommand(StaticFields.USER_EMAIL, StaticFields.NEW_PASS)).getClass(),
SessionDto.class
);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/registration/ChangePasswordCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.registration;
import com.tomo.ddd.domain.BusinessRuleValidationException;
import com.tomo.mcauthentication.application.authentication.command.EmailLoginCommand;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.contracts.Voidy;
import com.tomo.mcauthentication.application.registration.ChangePasswordCommandHandler;
import com.tomo.mcauthentication.application.registration.command.ChangePasswordCommand;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import com.tomo.mcauthentication.testdata.StaticFields;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
public class ChangePasswordCommandHandlerTest extends AbstractApplicationServiceTest {
@Autowired
ChangePasswordCommandHandler commandHandler;
@Test
@Transactional
public void testChangePassword() {
SessionDto sessionDto = formLogin();
assertEquals(commandHandler.handle(new ChangePasswordCommand(
sessionDto.getAccessToken(),
StaticFields.PASSWORD,
StaticFields.NEW_PASS,
StaticFields.NEW_PASS)).getClass(),
Voidy.class);
assertThrows(BusinessRuleValidationException.class, () -> emailLoginCommandHandler.handle(new EmailLoginCommand(StaticFields.USER_EMAIL, StaticFields.PASSWORD)));
assertEquals(
emailLoginCommandHandler.handle(new EmailLoginCommand(StaticFields.USER_EMAIL, StaticFields.NEW_PASS)).getClass(),
SessionDto.class
);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/registration/ConfirmUserRegistrationCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.registration;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import org.junit.Test;
import javax.transaction.Transactional;
import static org.junit.Assert.assertNotNull;
public class ConfirmUserRegistrationCommandHandlerTest extends AbstractApplicationServiceTest {
@Test
@Transactional
public void testConfirmUserRegistration() {
User user = createFormUser();
assertNotNull(user);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/registration/RegisterNewUserCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.registration;
import com.tomo.mcauthentication.application.registration.ConfirmUserRegistrationCommandHandler;
import com.tomo.mcauthentication.application.registration.NewUserRegisteredEventHandler;
import com.tomo.mcauthentication.application.registration.RegisterNewUserCommandHandler;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
public class RegisterNewUserCommandHandlerTest extends AbstractApplicationServiceTest {
@Autowired
RegisterNewUserCommandHandler commandHandler;
@Autowired
ConfirmUserRegistrationCommandHandler confirmUserRegistrationCommandHandler;
@Autowired
UserRegistrationRepository userRegistrationRepository;
@Autowired
NewUserRegisteredEventHandler newUserRegisteredEventHandler;
@Test
@Transactional
public void testNewUserRegistrationCreated() {
UserRegistration userRegistration = createUserRegistration();
assertNotNull(userRegistration);
verify(this.sendRegistrationConfirmationEmailCommandHandler, Mockito.times(1)).handle(any());
}
@Test
@Transactional
public void testNewUserRegistrationFailedWhenUserExists() {
createFormUser();
assertThrows(RuntimeException.class, () -> {
createUserRegistration();
});
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/users/ChangeUserDetailsCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.users;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.users.ChangeUserDetailsCommandHandler;
import com.tomo.mcauthentication.application.users.command.ChangeUserDetailsCommand;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserRepository;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import com.tomo.mcauthentication.testdata.CommandObjectMother;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertEquals;
public class ChangeUserDetailsCommandHandlerTest extends AbstractApplicationServiceTest {
@Autowired
ChangeUserDetailsCommandHandler commandHandler;
@Autowired
UserRepository userRepository;
@Test
@Transactional
public void testChangeUserDetails() {
User user = createFormUser();
SessionDto sessionDto = emailLoginCommandHandler.handle(CommandObjectMother.emailLoginCommand());
ChangeUserDetailsCommand command = new ChangeUserDetailsCommand(
user.getUserId().id(),
"first name",
"last name");
command.setAuthToken(sessionDto.getAccessToken());
commandHandler.handle(command);
User userFromDb = userRepository.findById(user.getUserId());
assertEquals(userFromDb.getFirstName(), "first name");
assertEquals(userFromDb.getLastName(), "last name");
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/integration/application/users/GetUserQueryHandlerTest.java
================================================
package com.tomo.mcauthentication.integration.application.users;
import com.tomo.mcauthentication.application.authentication.dto.SessionDto;
import com.tomo.mcauthentication.application.users.GetUserQueryHandler;
import com.tomo.mcauthentication.application.users.query.GetUserQuery;
import com.tomo.mcauthentication.integration.application.AbstractApplicationServiceTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.junit.Assert.assertNotNull;
public class GetUserQueryHandlerTest extends AbstractApplicationServiceTest {
@Autowired
GetUserQueryHandler getUserQueryHandler;
@Test
@Transactional
public void testGetUser() {
SessionDto sessionDto = formLogin();
GetUserQuery query = new GetUserQuery(sessionDto.getUserId());
assertNotNull(getUserQueryHandler.handle(query));
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/smoke/McAuthenticationApplicationSmokeTest.java
================================================
package com.tomo.mcauthentication.smoke;
import com.tomo.mcauthentication.infrastructure.springboot.controller.UserController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class McAuthenticationApplicationSmokeTest {
@Autowired
UserController userController;
@Test
void contextLoads() {
assertThat(userController).isNotNull();
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/testdata/CommandObjectMother.java
================================================
package com.tomo.mcauthentication.testdata;
import com.github.javafaker.Faker;
import com.tomo.mcauthentication.application.authentication.command.EmailLoginCommand;
import com.tomo.mcauthentication.application.authentication.command.FacebookLoginCommand;
import com.tomo.mcauthentication.application.authentication.command.GoogleLoginCommand;
import com.tomo.mcauthentication.application.recovery.command.CreatePasswordRecoveryCodeCommand;
import com.tomo.mcauthentication.application.registration.command.RegisterNewUserCommand;
public class CommandObjectMother extends StaticFields {
private static Faker faker = new Faker();
private CommandObjectMother() {
}
public static RegisterNewUserCommand registerNewUserCommand() {
return new RegisterNewUserCommand(USER_FIRST_NAME, USER_LAST_NAME, USER_EMAIL, PASSWORD);
}
public static RegisterNewUserCommand registerNewUserCommandWithFakerEmail() {
return new RegisterNewUserCommand(USER_FIRST_NAME, USER_LAST_NAME, faker.internet().emailAddress(), PASSWORD);
}
public static EmailLoginCommand emailLoginCommand() {
return new EmailLoginCommand(USER_EMAIL, PASSWORD);
}
public static FacebookLoginCommand facebookLoginCommand() {
return new FacebookLoginCommand(ACCESS_CODE);
}
public static GoogleLoginCommand googleLoginCommand() {
return new GoogleLoginCommand(ACCESS_CODE);
}
public static CreatePasswordRecoveryCodeCommand createPasswordRecoveryCodeCommand() {
return new CreatePasswordRecoveryCodeCommand(USER_EMAIL);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/testdata/StaticFields.java
================================================
package com.tomo.mcauthentication.testdata;
public class StaticFields {
public static final String ACCESS_CODE = "anAccessCode";
public static final String USER_OAUTH_ID = "anAccessCode";
public static final String USER_FIRST_NAME = "Tom";
public static final String USER_LAST_NAME = "Land";
public static final String USER_EMAIL = "random@email.com";
public static final String PASSWORD = "AA123bb##";
public static final String NEW_PASS = "randomNewPass123";
public static final String RECOVERY_CODE = "randomNewPass123";
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/application/registration/UserRegistrationCommandHandlerTest.java
================================================
package com.tomo.mcauthentication.unit.application.registration;
import com.tomo.mcauthentication.domain.DomainRegistry;
import com.tomo.mcauthentication.domain.EncryptionService;
import com.tomo.mcauthentication.domain.registration.PasswordService;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
import com.tomo.mcauthentication.domain.users.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@ActiveProfiles(profiles = { "unit" })
public class UserRegistrationCommandHandlerTest {
@Spy
DomainRegistry domainRegistry;
@Mock
UserRegistrationRepository userRegistrationRepository;
@Mock
UserRepository userRepository;
@Mock
EncryptionService encryptionService;
@Mock
ApplicationContext applicationContext;
protected MockitoSession mockito;
// @BeforeAll
// public void setup() {
// mockito = Mockito.mockitoSession()
// .initMocks(this)
// .strictness(Strictness.STRICT_STUBS)
// .startMocking();
// }
//
// @AfterAll
// public void tearDown() {
// mockito.finishMocking();
// }
@BeforeEach
public void setUp(){
domainRegistry.setApplicationContext(applicationContext);
// when(encryptionService.encryptedValue(any())).thenReturn("randomString");
when(applicationContext.getBean("passwordService")).thenReturn(new PasswordService());
when(applicationContext.getBean("MD5EncryptionService")).thenReturn(encryptionService);
when(applicationContext.getBean("userRepository")).thenReturn(userRepository);
when(applicationContext.getBean("userRegistrationRepository")).thenReturn(userRegistrationRepository);
DomainRegistry.encryptionService();
int a = 5;
}
@Test
public void testCreateUserRegister() {
when(userRegistrationRepository.countByEmailAndStatus(any(), any())).thenReturn(Long.valueOf(0));
when(userRepository.findByEmail(any())).thenReturn(null);
UserRegistration ur = UserRegistration.registerNewUser("AA123bb##", "email", "firstName", "lastName");
assertEquals(ur.getEmail(), "email");
assertEquals(ur.getStatus(), UserRegistrationStatus.WaitingForConfirmation);
assertNotEquals(ur.getEmail(), "email1");
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/AbstractUnitTest.java
================================================
package com.tomo.mcauthentication.unit.domain;
import org.junit.jupiter.api.TestInstance;
import org.junit.runner.RunWith;
import org.mockito.MockitoSession;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.context.ActiveProfiles;
@RunWith(MockitoJUnitRunner.StrictStubs.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ActiveProfiles(profiles = { "unit" })
public abstract class AbstractUnitTest {
protected MockitoSession mockito;
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/UserRegistrationTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration;
import com.tomo.ddd.domain.DomainEventPublisher;
import com.tomo.mcauthentication.domain.DomainRegistry;
import com.tomo.mcauthentication.domain.EncryptionService;
import com.tomo.mcauthentication.domain.registration.PasswordService;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
import com.tomo.mcauthentication.domain.registration.events.*;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserId;
import com.tomo.mcauthentication.domain.users.UserRepository;
import com.tomo.mcauthentication.testdata.StaticFields;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import java.time.LocalDateTime;
import java.util.UUID;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ActiveProfiles(profiles = { "unit" })
public class UserRegistrationTest {
private static boolean setUpIsDone = false;
private static final String WEAK_PASS = "abc";
DomainRegistry domainRegistry;
@Mock
UserRegistrationRepository userRegistrationRepository;
@Mock
UserRepository userRepository;
@Mock
EncryptionService encryptionService;
@Mock
ApplicationContext applicationContext;
@Mock
DomainEventPublisher domainEventPublisher;
MockedStatic publisher;
MockedConstruction mockedUser;
@BeforeEach
public void setUp() {
publisher = Mockito.mockStatic(DomainEventPublisher.class);
publisher.when(DomainEventPublisher::instance).thenReturn(domainEventPublisher);
mockedUser = Mockito.mockConstruction(User.class);
domainRegistry = new DomainRegistry();
domainRegistry.setApplicationContext(applicationContext);
when(applicationContext.getBean("passwordService")).thenReturn(new PasswordService());
when(applicationContext.getBean("MD5EncryptionService")).thenReturn(encryptionService);
when(applicationContext.getBean("userRepository")).thenReturn(userRepository);
when(applicationContext.getBean("userRegistrationRepository")).thenReturn(userRegistrationRepository);
when(encryptionService.encryptedValue(anyString())).thenReturn("randomEncrypted");
when(encryptionService.encryptedValue(WEAK_PASS)).thenReturn("weekPassEncrypted");
when(encryptionService.encryptedValue(StaticFields.PASSWORD)).thenReturn("currentPassEncrypted");
when(encryptionService.encryptedValue(StaticFields.NEW_PASS)).thenReturn("newPassEncrypted");
when(userRegistrationRepository.countByEmailAndStatus(any(), any())).thenReturn(Long.valueOf(0));
when(userRepository.findByEmail(any())).thenReturn(null);
setUpIsDone = true;
}
@AfterEach
public void after() {
mockedUser.close();
publisher.close();
Mockito.reset(domainEventPublisher);
}
@Order(1)
@Test
public void testCreateUserRegistration() {
UserRegistration ur = userRegistration();
assertEquals(ur.getEmail(), StaticFields.USER_EMAIL);
assertEquals(ur.getFirstName(), StaticFields.USER_FIRST_NAME);
assertEquals(ur.getLastName(), StaticFields.USER_LAST_NAME);
assertEquals(ur.getStatus(), UserRegistrationStatus.WaitingForConfirmation);
assertNotEquals(ur.getPassword(), StaticFields.PASSWORD);
assertNotNull(ur.getConfirmationCode());
verify(domainEventPublisher, times(1)).publish(isA(UserRegistrationRequested.class));
}
@Test
@Order(2)
public void testCreateUserFromUserRegistration() {
UserId userId = new UserId(UUID.randomUUID());
when(userRepository.nextIdentity()).thenReturn(userId);
UserRegistration ur = userRegistration();
ur.createUser(userRepository);
assertEquals(ur.getStatus(), UserRegistrationStatus.Confirmed);
assertEquals(ur.getUserId(), userId);
verify(domainEventPublisher, times(1)).publish(isA(UserRegistrationConfirmed.class));
}
@Test
@Order(3)
public void testCreateRecoveryCodeAndRecoverPassword() {
UserId userId = new UserId(UUID.randomUUID());
when(userRepository.nextIdentity()).thenReturn(userId);
UserRegistration ur = userRegistration();
ur.createUser(userRepository);
String recoveryCode = ur.createRecoveryCode();
assertNotNull(ur.getRecoveryCode());
assertTrue(ur.getRecoveryCode().length() > 0);
assertNotNull(ur.getRecoveryCodeExpirationDate());
assertTrue(ur.getRecoveryCodeExpirationDate().isAfter(LocalDateTime.now()));
verify(domainEventPublisher, times(1)).publish(isA(PasswordRecoveryCodeCreated.class));
assertThrows(RuntimeException.class, () -> {
ur.changePasswordWithRecoveryCode(recoveryCode, StaticFields.NEW_PASS, StaticFields.PASSWORD); //repeted pass not equal
});
assertThrows(RuntimeException.class, () -> {
ur.changePasswordWithRecoveryCode(recoveryCode, StaticFields.PASSWORD, StaticFields.PASSWORD); //same pass
});
assertThrows(RuntimeException.class, () -> {
ur.changePasswordWithRecoveryCode(recoveryCode, WEAK_PASS, WEAK_PASS); //weak pass
});
assertThrows(RuntimeException.class, () -> {
ur.changePasswordWithRecoveryCode(recoveryCode, StaticFields.USER_EMAIL, StaticFields.USER_EMAIL); //pass not equal to email
});
ur.changePasswordWithRecoveryCode(recoveryCode, StaticFields.NEW_PASS, StaticFields.NEW_PASS);
verify(domainEventPublisher, times(1)).publish(isA(PasswordRecovered.class));
}
@Test
@Order(4)
public void testChangePassword() {
UserId userId = new UserId(UUID.randomUUID());
when(userRepository.nextIdentity()).thenReturn(userId);
UserRegistration ur = userRegistration();
ur.createUser(userRepository);
String oldPassword = ur.getPassword();
assertThrows(RuntimeException.class, () -> {
ur.changePassword(StaticFields.PASSWORD, StaticFields.NEW_PASS, StaticFields.PASSWORD); //repeted pass not equal
});
assertThrows(RuntimeException.class, () -> {
ur.changePassword(StaticFields.PASSWORD, StaticFields.PASSWORD, StaticFields.PASSWORD); //same pass
});
assertThrows(RuntimeException.class, () -> {
ur.changePassword(StaticFields.PASSWORD, WEAK_PASS, WEAK_PASS); //weak pass
});
assertThrows(RuntimeException.class, () -> {
ur.changePassword(StaticFields.PASSWORD, StaticFields.USER_EMAIL, StaticFields.USER_EMAIL); //pass not equal to email
});
ur.changePassword(StaticFields.PASSWORD, StaticFields.NEW_PASS, StaticFields.NEW_PASS);
assertNotEquals(oldPassword, ur.getPassword());
assertEquals(this.encryptionService.encryptedValue(StaticFields.NEW_PASS), ur.getPassword());
verify(domainEventPublisher, times(1)).publish(isA(PasswordChanged.class));
}
private UserRegistration userRegistration() {
return UserRegistration.registerNewUser(
StaticFields.PASSWORD,
StaticFields.USER_EMAIL,
StaticFields.USER_FIRST_NAME,
StaticFields.USER_LAST_NAME);
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/rules/PasswordRecoveryCodeShouldBeExpiredOrNullTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration.rules;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.rules.PasswordRecoveryCodeShouldBeExpiredOrNull;
import com.tomo.mcauthentication.unit.domain.AbstractUnitTest;
import org.junit.Test;
import java.time.LocalDateTime;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class PasswordRecoveryCodeShouldBeExpiredOrNullTest extends AbstractUnitTest {
@Test
public void testRuleIsNotBroken() {
UserRegistration userRegistration = new UserRegistration();
userRegistration.setRecoveryCodeExpirationDate(LocalDateTime.now().minusSeconds(60));
assertTrue((new PasswordRecoveryCodeShouldBeExpiredOrNull(userRegistration)).isRuleComplied());
assertTrue((new PasswordRecoveryCodeShouldBeExpiredOrNull(new UserRegistration())).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
UserRegistration userRegistration = new UserRegistration();
userRegistration.setRecoveryCodeExpirationDate(LocalDateTime.now().plusSeconds(60));
userRegistration.setRecoveryCode("123");
assertFalse((new PasswordRecoveryCodeShouldBeExpiredOrNull(userRegistration)).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/rules/PasswordRecoveryCodeShouldNotExpiredTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration.rules;
import com.tomo.mcauthentication.domain.registration.UserRegistration;
import com.tomo.mcauthentication.domain.registration.rules.PasswordRecoveryCodeShouldNotExpired;
import com.tomo.mcauthentication.unit.domain.AbstractUnitTest;
import org.junit.Test;
import java.time.LocalDateTime;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class PasswordRecoveryCodeShouldNotExpiredTest extends AbstractUnitTest {
@Test
public void testRuleIsNotBroken() {
UserRegistration userRegistration = new UserRegistration();
userRegistration.setRecoveryCodeExpirationDate(LocalDateTime.now().plusSeconds(60));
assertTrue((new PasswordRecoveryCodeShouldNotExpired(userRegistration)).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
UserRegistration userRegistration = new UserRegistration();
userRegistration.setRecoveryCodeExpirationDate(LocalDateTime.now());
assertFalse((new PasswordRecoveryCodeShouldNotExpired(userRegistration)).isRuleComplied());
assertFalse((new PasswordRecoveryCodeShouldNotExpired(new UserRegistration())).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/rules/PasswordsMustMatchTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration.rules;
import com.tomo.mcauthentication.domain.registration.rules.PasswordsMustMatch;
import com.tomo.mcauthentication.unit.domain.AbstractUnitTest;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class PasswordsMustMatchTest extends AbstractUnitTest {
@Test
public void testRuleIsNotBroken() {
assertTrue((new PasswordsMustMatch("abc", "abc")).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
assertFalse((new PasswordsMustMatch("abc1", "abc")).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/rules/RecoveryCodeMustMatchTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration.rules;
import com.tomo.mcauthentication.domain.registration.rules.RecoveryCodeMustMatch;
import com.tomo.mcauthentication.unit.domain.AbstractUnitTest;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class RecoveryCodeMustMatchTest extends AbstractUnitTest {
@Test
public void testRuleIsNotBroken() {
assertTrue((new RecoveryCodeMustMatch("abc", "abc")).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
assertFalse((new RecoveryCodeMustMatch("abc1", "abc")).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/rules/UserRegistrationCannotBeConfirmedAfterExpirationTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration.rules;
import com.tomo.mcauthentication.domain.registration.rules.UserRegistrationCannotBeConfirmedAfterExpiration;
import com.tomo.mcauthentication.unit.domain.AbstractUnitTest;
import org.junit.Test;
import java.time.LocalDateTime;
import static com.tomo.mcauthentication.domain.registration.rules.UserRegistrationCannotBeConfirmedAfterExpiration.CONFIRMATION_LINK_DURATION;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class UserRegistrationCannotBeConfirmedAfterExpirationTest extends AbstractUnitTest {
@Test
public void testRuleIsNotBroken() {
LocalDateTime registerDate1 = LocalDateTime.now();
assertTrue((new UserRegistrationCannotBeConfirmedAfterExpiration(registerDate1)).isRuleComplied());
LocalDateTime registerDate2 = LocalDateTime.now().minusDays(CONFIRMATION_LINK_DURATION).plusSeconds(5);
assertTrue((new UserRegistrationCannotBeConfirmedAfterExpiration(registerDate2)).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
LocalDateTime registerDate1 = LocalDateTime.now().minusDays(CONFIRMATION_LINK_DURATION);
assertFalse((new UserRegistrationCannotBeConfirmedAfterExpiration(registerDate1)).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/rules/UserRegistrationCannotBeConfirmedMoreThanOnceTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration.rules;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
import com.tomo.mcauthentication.domain.registration.rules.UserRegistrationCannotBeConfirmedMoreThanOnce;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class UserRegistrationCannotBeConfirmedMoreThanOnceTest {
@Test
public void testRuleIsNotBroken() {
assertTrue((new UserRegistrationCannotBeConfirmedMoreThanOnce(UserRegistrationStatus.WaitingForConfirmation)).isRuleComplied());
assertTrue((new UserRegistrationCannotBeConfirmedMoreThanOnce(UserRegistrationStatus.Expired)).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
assertFalse((new UserRegistrationCannotBeConfirmedMoreThanOnce(UserRegistrationStatus.Confirmed)).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/rules/UserRegistrationMustBeConfirmedTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration.rules;
import com.tomo.mcauthentication.domain.registration.UserRegistrationStatus;
import com.tomo.mcauthentication.domain.registration.rules.UserRegistrationMustBeConfirmed;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class UserRegistrationMustBeConfirmedTest {
@Test
public void testRuleIsNotBroken() {
assertTrue((new UserRegistrationMustBeConfirmed(UserRegistrationStatus.Confirmed)).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
assertFalse((new UserRegistrationMustBeConfirmed(UserRegistrationStatus.WaitingForConfirmation)).isRuleComplied());
assertFalse((new UserRegistrationMustBeConfirmed(UserRegistrationStatus.Expired)).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/registration/rules/UserRegistrationMustBeUniqueTest.java
================================================
package com.tomo.mcauthentication.unit.domain.registration.rules;
import com.tomo.mcauthentication.domain.registration.UserRegistrationRepository;
import com.tomo.mcauthentication.domain.registration.rules.UserRegistrationMustBeUnique;
import com.tomo.mcauthentication.unit.domain.AbstractUnitTest;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.when;
public class UserRegistrationMustBeUniqueTest extends AbstractUnitTest {
UserRegistrationRepository repository = Mockito.mock(UserRegistrationRepository.class);
@Test
public void testRuleIsNotBroken() {
when(repository.countByEmailAndStatus(any(), any())).thenReturn(0L);
assertTrue((new UserRegistrationMustBeUnique(repository, "")).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
when(repository.countByEmailAndStatus(any(), any())).thenReturn(1L);
assertFalse((new UserRegistrationMustBeUnique(repository, "")).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/session/rules/SessionCannotBeExpiredWhenRefreshTokenIsMissingTest.java
================================================
package com.tomo.mcauthentication.unit.domain.session.rules;
import com.tomo.mcauthentication.domain.session.Session;
import com.tomo.mcauthentication.domain.session.rule.SessionCannotBeExpiredWhenRefreshTokenIsMissing;
import com.tomo.mcauthentication.unit.domain.AbstractUnitTest;
import org.junit.Test;
import java.time.LocalDateTime;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class SessionCannotBeExpiredWhenRefreshTokenIsMissingTest extends AbstractUnitTest {
@Test
public void testRuleIsNotBroken() {
Session session1 = new Session();
session1.setExpirationDate(LocalDateTime.now().plusDays(2));
Session session2 = new Session();
session2.setExpirationDate(LocalDateTime.now().minusDays(2));
session2.setRefreshToken("randomToken");
assertTrue((new SessionCannotBeExpiredWhenRefreshTokenIsMissing(session1)).isRuleComplied());
assertTrue((new SessionCannotBeExpiredWhenRefreshTokenIsMissing(session2)).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
Session session1 = new Session();
session1.setExpirationDate(LocalDateTime.now().minusDays(2));
assertFalse((new SessionCannotBeExpiredWhenRefreshTokenIsMissing(session1)).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/unit/domain/user/rules/UserEmailMustBeUniqueTest.java
================================================
package com.tomo.mcauthentication.unit.domain.user.rules;
import com.tomo.mcauthentication.domain.users.User;
import com.tomo.mcauthentication.domain.users.UserRepository;
import com.tomo.mcauthentication.domain.users.rules.UserEmailMustBeUnique;
import com.tomo.mcauthentication.unit.domain.AbstractUnitTest;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
public class UserEmailMustBeUniqueTest extends AbstractUnitTest {
UserRepository repository = Mockito.mock(UserRepository.class);
@Test
public void testRuleIsNotBroken() {
when(repository.findByEmail(any())).thenReturn(null);
assertTrue((new UserEmailMustBeUnique(repository, "random@email.com")).isRuleComplied());
}
@Test
public void testRuleIsBroken() {
when(repository.findByEmail(any())).thenReturn(new User());
assertFalse((new UserEmailMustBeUnique(repository, "random@email.com")).isRuleComplied());
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/weblayer/BaseWebLayerTest.java
================================================
package com.tomo.mcauthentication.weblayer;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Profile;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class BaseWebLayerTest { }
================================================
FILE: src/test/java/com/tomo/mcauthentication/weblayer/springboot/controller/AbstractControllerTest.java
================================================
package com.tomo.mcauthentication.weblayer.springboot.controller;
import com.tomo.ddd.email.EmailSender;
import com.tomo.mcauthentication.weblayer.BaseWebLayerTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpHeaders;
import org.springframework.test.web.servlet.MockMvc;
import java.net.URI;
import java.net.URISyntaxException;
@AutoConfigureMockMvc
public abstract class AbstractControllerTest extends BaseWebLayerTest {
@MockBean
protected EmailSender emailMessageSender;
@LocalServerPort
protected int randomServerPort;
@Autowired
protected MockMvc mockMvc;
protected URI url(String uri) throws URISyntaxException {
final String baseUrl = "http://localhost:" + randomServerPort+ uri;
return new URI(baseUrl);
}
protected HttpHeaders baseHeaders() {
HttpHeaders headers = new org.springframework.http.HttpHeaders();
headers.set("X-COM-PERSIST", "true");
return headers;
}
}
================================================
FILE: src/test/java/com/tomo/mcauthentication/weblayer/springboot/controller/RegistrationControllerTest.java
================================================
package com.tomo.mcauthentication.weblayer.springboot.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tomo.mcauthentication.application.registration.command.RegisterNewUserCommand;
import com.tomo.mcauthentication.infrastructure.springboot.controller.RestApiRoutes.RegistrationRoutes;
import com.tomo.mcauthentication.testdata.CommandObjectMother;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class RegistrationControllerTest extends AbstractControllerTest {
@Autowired
private ObjectMapper objectMapper;
@Test
@Transactional
public void shouldCreateUserRegistration() throws Exception {
RegisterNewUserCommand command = CommandObjectMother.registerNewUserCommandWithFakerEmail();
this.mockMvc.perform(
post(RegistrationRoutes.FORM_REGISTRATION)
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(command)))
.andDo(print())
.andExpect(status().isCreated());
}
}
================================================
FILE: src/test/resources/application-ci.yml
================================================
debug: true
spring:
datasource:
url: jdbc:postgresql://mc-authentication-db-test:5432/mc_authentication