Initial version of sasiedzi generated by generator-jhipster@8.7.2
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java/.devcontainer/base.Dockerfile
|
||||||
|
|
||||||
|
# [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 17, 17-bullseye, 17-buster
|
||||||
|
ARG VARIANT="17"
|
||||||
|
FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT}
|
||||||
|
|
||||||
|
# [Option] Install Maven
|
||||||
|
ARG INSTALL_MAVEN="false"
|
||||||
|
ARG MAVEN_VERSION=""
|
||||||
|
# [Option] Install Gradle
|
||||||
|
ARG INSTALL_GRADLE="false"
|
||||||
|
ARG GRADLE_VERSION=""
|
||||||
|
RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \
|
||||||
|
&& if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi
|
||||||
|
|
||||||
|
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
|
||||||
|
ARG NODE_VERSION="none"
|
||||||
|
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
|
||||||
|
|
||||||
|
# [Optional] Uncomment this section to install additional OS packages.
|
||||||
|
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
|
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||||
|
|
||||||
|
# [Optional] Uncomment this line to install global node packages.
|
||||||
|
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||||
|
// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java
|
||||||
|
{
|
||||||
|
"name": "Sasiedzi",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
"args": {
|
||||||
|
// Update the VARIANT arg to pick a Java version: 17, 19
|
||||||
|
// Append -bullseye or -buster to pin to an OS version.
|
||||||
|
// Use the -bullseye variants on local arm64/Apple Silicon.
|
||||||
|
"VARIANT": "17-bullseye",
|
||||||
|
// Options
|
||||||
|
// maven and gradle wrappers are used by default, we don't need them installed globally
|
||||||
|
// "INSTALL_MAVEN": "true",
|
||||||
|
// "INSTALL_GRADLE": "false",
|
||||||
|
"NODE_VERSION": "20.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
// Set *default* container specific settings.json values on container create.
|
||||||
|
"settings": {
|
||||||
|
"java.jdt.ls.java.home": "/docker-java-home"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
|
"extensions": [
|
||||||
|
"christian-kohler.npm-intellisense",
|
||||||
|
"firsttris.vscode-jest-runner",
|
||||||
|
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"vscjava.vscode-java-pack",
|
||||||
|
"pivotal.vscode-boot-dev-pack",
|
||||||
|
"esbenp.prettier-vscode"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
"forwardPorts": [9060, 3001, 9000, 8080],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "java -version",
|
||||||
|
|
||||||
|
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||||
|
"remoteUser": "vscode",
|
||||||
|
"features": {
|
||||||
|
"docker-in-docker": "latest",
|
||||||
|
"docker-from-docker": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# EditorConfig helps developers define and maintain consistent
|
||||||
|
# coding styles between different editors and IDEs
|
||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
|
||||||
|
# We recommend you to keep these unchanged
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# Change these settings to your own preference
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
# Generated by jhipster:java:bootstrap generator
|
||||||
|
[*.java]
|
||||||
|
indent_size = 4
|
||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
# This file is inspired by https://github.com/alexkaratarakis/gitattributes
|
||||||
|
#
|
||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# The above will handle all files NOT found below
|
||||||
|
# These files are text and should be normalized (Convert crlf => lf)
|
||||||
|
|
||||||
|
*.bat text eol=crlf
|
||||||
|
*.cmd text eol=crlf
|
||||||
|
*.ps1 text eol=crlf
|
||||||
|
*.coffee text
|
||||||
|
*.css text
|
||||||
|
*.cql text
|
||||||
|
*.df text
|
||||||
|
*.ejs text
|
||||||
|
*.html text
|
||||||
|
*.java text
|
||||||
|
*.js text
|
||||||
|
*.json text
|
||||||
|
*.less text
|
||||||
|
*.properties text
|
||||||
|
*.sass text
|
||||||
|
*.scss text
|
||||||
|
*.sh text eol=lf
|
||||||
|
*.sql text
|
||||||
|
*.txt text
|
||||||
|
*.ts text
|
||||||
|
*.xml text
|
||||||
|
*.yaml text
|
||||||
|
*.yml text
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
*.doc diff=astextplain
|
||||||
|
*.DOC diff=astextplain
|
||||||
|
*.docx diff=astextplain
|
||||||
|
*.DOCX diff=astextplain
|
||||||
|
*.dot diff=astextplain
|
||||||
|
*.DOT diff=astextplain
|
||||||
|
*.pdf diff=astextplain
|
||||||
|
*.PDF diff=astextplain
|
||||||
|
*.rtf diff=astextplain
|
||||||
|
*.RTF diff=astextplain
|
||||||
|
*.markdown text
|
||||||
|
*.md text
|
||||||
|
*.adoc text
|
||||||
|
*.textile text
|
||||||
|
*.mustache text
|
||||||
|
*.csv text
|
||||||
|
*.tab text
|
||||||
|
*.tsv text
|
||||||
|
*.txt text
|
||||||
|
AUTHORS text
|
||||||
|
CHANGELOG text
|
||||||
|
CHANGES text
|
||||||
|
CONTRIBUTING text
|
||||||
|
COPYING text
|
||||||
|
copyright text
|
||||||
|
*COPYRIGHT* text
|
||||||
|
INSTALL text
|
||||||
|
license text
|
||||||
|
LICENSE text
|
||||||
|
NEWS text
|
||||||
|
readme text
|
||||||
|
*README* text
|
||||||
|
TODO text
|
||||||
|
|
||||||
|
# Graphics
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.gif binary
|
||||||
|
*.tif binary
|
||||||
|
*.tiff binary
|
||||||
|
*.ico binary
|
||||||
|
# SVG treated as an asset (binary) by default. If you want to treat it as text,
|
||||||
|
# comment-out the following line and uncomment the line after.
|
||||||
|
*.svg binary
|
||||||
|
#*.svg text
|
||||||
|
*.eps binary
|
||||||
|
|
||||||
|
# These files are binary and should be left untouched
|
||||||
|
# (binary is a macro for -text -diff)
|
||||||
|
*.class binary
|
||||||
|
*.jar binary
|
||||||
|
*.war binary
|
||||||
|
|
||||||
|
## LINTERS
|
||||||
|
.csslintrc text
|
||||||
|
.eslintrc text
|
||||||
|
.jscsrc text
|
||||||
|
.jshintrc text
|
||||||
|
.jshintignore text
|
||||||
|
.stylelintrc text
|
||||||
|
|
||||||
|
## CONFIGS
|
||||||
|
*.conf text
|
||||||
|
*.config text
|
||||||
|
.editorconfig text
|
||||||
|
.gitattributes text
|
||||||
|
.gitconfig text
|
||||||
|
.gitignore text
|
||||||
|
.htaccess text
|
||||||
|
*.npmignore text
|
||||||
|
|
||||||
|
## HEROKU
|
||||||
|
Procfile text
|
||||||
|
.slugignore text
|
||||||
|
|
||||||
|
## AUDIO
|
||||||
|
*.kar binary
|
||||||
|
*.m4a binary
|
||||||
|
*.mid binary
|
||||||
|
*.midi binary
|
||||||
|
*.mp3 binary
|
||||||
|
*.ogg binary
|
||||||
|
*.ra binary
|
||||||
|
|
||||||
|
## VIDEO
|
||||||
|
*.3gpp binary
|
||||||
|
*.3gp binary
|
||||||
|
*.as binary
|
||||||
|
*.asf binary
|
||||||
|
*.asx binary
|
||||||
|
*.fla binary
|
||||||
|
*.flv binary
|
||||||
|
*.m4v binary
|
||||||
|
*.mng binary
|
||||||
|
*.mov binary
|
||||||
|
*.mp4 binary
|
||||||
|
*.mpeg binary
|
||||||
|
*.mpg binary
|
||||||
|
*.swc binary
|
||||||
|
*.swf binary
|
||||||
|
*.webm binary
|
||||||
|
|
||||||
|
## ARCHIVES
|
||||||
|
*.7z binary
|
||||||
|
*.gz binary
|
||||||
|
*.rar binary
|
||||||
|
*.tar binary
|
||||||
|
*.zip binary
|
||||||
|
|
||||||
|
## FONTS
|
||||||
|
*.ttf binary
|
||||||
|
*.eot binary
|
||||||
|
*.otf binary
|
||||||
|
*.woff binary
|
||||||
|
*.woff2 binary
|
||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
######################
|
||||||
|
# Node
|
||||||
|
######################
|
||||||
|
/node/
|
||||||
|
node_tmp/
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log.*
|
||||||
|
/.awcache/*
|
||||||
|
/.cache-loader/*
|
||||||
|
|
||||||
|
######################
|
||||||
|
# SASS
|
||||||
|
######################
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Eclipse
|
||||||
|
######################
|
||||||
|
*.pydevproject
|
||||||
|
.project
|
||||||
|
.metadata
|
||||||
|
tmp/
|
||||||
|
tmp/**/*
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/**
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# CDT-specific
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# PDT-specific
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# STS-specific
|
||||||
|
/.sts4-cache/*
|
||||||
|
|
||||||
|
######################
|
||||||
|
# IntelliJ
|
||||||
|
######################
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
*.ipr
|
||||||
|
*.ids
|
||||||
|
*.orig
|
||||||
|
classes/
|
||||||
|
out/
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Visual Studio Code
|
||||||
|
######################
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Maven
|
||||||
|
######################
|
||||||
|
/log/
|
||||||
|
/target/
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Gradle
|
||||||
|
######################
|
||||||
|
.gradle/
|
||||||
|
/build/
|
||||||
|
/buildSrc/.gradle/
|
||||||
|
/buildSrc/build/
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Package Files
|
||||||
|
######################
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
*.db
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Windows
|
||||||
|
######################
|
||||||
|
# Windows image file caches
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Mac OSX
|
||||||
|
######################
|
||||||
|
.DS_Store
|
||||||
|
.svn
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear on external disk
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Directories
|
||||||
|
######################
|
||||||
|
/bin/
|
||||||
|
/deploy/
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Logs
|
||||||
|
######################
|
||||||
|
*.log*
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Others
|
||||||
|
######################
|
||||||
|
*.class
|
||||||
|
*.*~
|
||||||
|
*~
|
||||||
|
.merge_file*
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Gradle Wrapper
|
||||||
|
######################
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Maven Wrapper
|
||||||
|
######################
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
######################
|
||||||
|
# ESLint
|
||||||
|
######################
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Code coverage
|
||||||
|
######################
|
||||||
|
/coverage/
|
||||||
|
/.nyc_output/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
lint-staged
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
'{,**/}*.{md,json,yml,js,cjs,mjs,ts,cts,mts,java,html,vue,css,scss}': ['prettier --write'],
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Vendored
BIN
Binary file not shown.
+18
@@ -0,0 +1,18 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
wrapperVersion=3.3.2
|
||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
'postcss-import': {},
|
||||||
|
'postcss-url': {},
|
||||||
|
// to edit target browsers: use "browserslist" field in package.json
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
.git
|
||||||
|
|
||||||
|
# Generated by jhipster:maven
|
||||||
|
target
|
||||||
|
.mvn
|
||||||
|
|
||||||
|
# Generated by jhipster:client
|
||||||
|
target/classes/static/
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
printWidth: 140
|
||||||
|
singleQuote: true
|
||||||
|
tabWidth: 2
|
||||||
|
useTabs: false
|
||||||
|
arrowParens: avoid
|
||||||
|
bracketSameLine: false
|
||||||
|
plugins:
|
||||||
|
- prettier-plugin-packagejson
|
||||||
|
- prettier-plugin-java
|
||||||
|
overrides:
|
||||||
|
- files: "*.java"
|
||||||
|
options:
|
||||||
|
tabWidth: 4
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"generator-jhipster": {
|
||||||
|
"applicationType": "monolith",
|
||||||
|
"authenticationType": "oauth2",
|
||||||
|
"baseName": "sasiedzi",
|
||||||
|
"buildTool": "maven",
|
||||||
|
"cacheProvider": "no",
|
||||||
|
"clientFramework": "vue",
|
||||||
|
"clientTestFrameworks": [],
|
||||||
|
"clientTheme": "none",
|
||||||
|
"creationTimestamp": 1730225637495,
|
||||||
|
"databaseType": "sql",
|
||||||
|
"devDatabaseType": "postgresql",
|
||||||
|
"devServerPort": 9060,
|
||||||
|
"enableHibernateCache": null,
|
||||||
|
"enableSwaggerCodegen": false,
|
||||||
|
"enableTranslation": false,
|
||||||
|
"entities": [],
|
||||||
|
"feignClient": null,
|
||||||
|
"jhipsterVersion": "8.7.2",
|
||||||
|
"messageBroker": false,
|
||||||
|
"microfrontend": null,
|
||||||
|
"microfrontends": [],
|
||||||
|
"nativeLanguage": "en",
|
||||||
|
"packageName": "com.sasiedzi.event",
|
||||||
|
"prodDatabaseType": "postgresql",
|
||||||
|
"reactive": false,
|
||||||
|
"searchEngine": false,
|
||||||
|
"serverPort": null,
|
||||||
|
"serverSideOptions": [],
|
||||||
|
"serviceDiscoveryType": false,
|
||||||
|
"skipUserManagement": true,
|
||||||
|
"syncUserWithIdp": true,
|
||||||
|
"testFrameworks": [],
|
||||||
|
"websocket": false,
|
||||||
|
"withAdminUi": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,382 @@
|
|||||||
|
# sasiedzi
|
||||||
|
|
||||||
|
This application was generated using JHipster 8.7.2, you can find documentation and help at [https://www.jhipster.tech/documentation-archive/v8.7.2](https://www.jhipster.tech/documentation-archive/v8.7.2).
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
Node is required for generation and recommended for development. `package.json` is always generated for a better development experience with prettier, commit hooks, scripts and so on.
|
||||||
|
|
||||||
|
In the project root, JHipster generates configuration files for tools like git, prettier, eslint, husky, and others that are well known and you can find references in the web.
|
||||||
|
|
||||||
|
`/src/*` structure follows default Java structure.
|
||||||
|
|
||||||
|
- `.yo-rc.json` - Yeoman configuration file
|
||||||
|
JHipster configuration is stored in this file at `generator-jhipster` key. You may find `generator-jhipster-*` for specific blueprints configuration.
|
||||||
|
- `.yo-resolve` (optional) - Yeoman conflict resolver
|
||||||
|
Allows to use a specific action when conflicts are found skipping prompts for files that matches a pattern. Each line should match `[pattern] [action]` with pattern been a [Minimatch](https://github.com/isaacs/minimatch#minimatch) pattern and action been one of skip (default if omitted) or force. Lines starting with `#` are considered comments and are ignored.
|
||||||
|
- `.jhipster/*.json` - JHipster entity configuration files
|
||||||
|
|
||||||
|
- `npmw` - wrapper to use locally installed npm.
|
||||||
|
JHipster installs Node and npm locally using the build tool by default. This wrapper makes sure npm is installed locally and uses it avoiding some differences different versions can cause. By using `./npmw` instead of the traditional `npm` you can configure a Node-less environment to develop or test your application.
|
||||||
|
- `/src/main/docker` - Docker configurations for the application and services that the application depends on
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### OAuth 2.0 / OpenID Connect
|
||||||
|
|
||||||
|
Congratulations! You've selected an excellent way to secure your JHipster application. If you're not sure what OAuth and OpenID Connect (OIDC) are, please see [What the Heck is OAuth?](https://developer.okta.com/blog/2017/06/21/what-the-heck-is-oauth)
|
||||||
|
|
||||||
|
To log in to your app, you'll need to have [Keycloak](https://keycloak.org) up and running. The JHipster Team has created a Docker container for you that has the default users and roles. Start Keycloak using the following command.
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose -f src/main/docker/keycloak.yml up
|
||||||
|
```
|
||||||
|
|
||||||
|
The security settings in `src/main/resources/config/application.yml` are configured for this image.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spring:
|
||||||
|
...
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
client:
|
||||||
|
provider:
|
||||||
|
oidc:
|
||||||
|
issuer-uri: http://localhost:9080/realms/jhipster
|
||||||
|
registration:
|
||||||
|
oidc:
|
||||||
|
client-id: web_app
|
||||||
|
client-secret: web_app
|
||||||
|
scope: openid,profile,email
|
||||||
|
```
|
||||||
|
|
||||||
|
Some of Keycloak configuration is now done in build time and the other part before running the app, here is the [list](https://www.keycloak.org/server/all-config) of all build and configuration options.
|
||||||
|
|
||||||
|
Before moving to production, please make sure to follow this [guide](https://www.keycloak.org/server/configuration) for better security and performance.
|
||||||
|
|
||||||
|
Also, you should never use `start-dev` nor `KC_DB=dev-file` in production.
|
||||||
|
|
||||||
|
When using Kubernetes, importing should be done using init-containers (with a volume when using `db=dev-file`).
|
||||||
|
|
||||||
|
### Okta
|
||||||
|
|
||||||
|
If you'd like to use Okta instead of Keycloak, it's pretty quick using the [Okta CLI](https://cli.okta.com/). After you've installed it, run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
okta register
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, in your JHipster app's directory, run `okta apps create` and select **JHipster**. This will set up an Okta app for you, create `ROLE_ADMIN` and `ROLE_USER` groups, create a `.okta.env` file with your Okta settings, and configure a `groups` claim in your ID token.
|
||||||
|
|
||||||
|
Run `source .okta.env` and start your app with Maven or Gradle. You should be able to sign in with the credentials you registered with.
|
||||||
|
|
||||||
|
If you're on Windows, you should install [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) so the `source` command will work.
|
||||||
|
|
||||||
|
If you'd like to configure things manually through the Okta developer console, see the instructions below.
|
||||||
|
|
||||||
|
First, you'll need to create a free developer account at <https://developer.okta.com/signup/>. After doing so, you'll get your own Okta domain, that has a name like `https://dev-123456.okta.com`.
|
||||||
|
|
||||||
|
Modify `src/main/resources/config/application.yml` to use your Okta settings.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spring:
|
||||||
|
...
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
client:
|
||||||
|
provider:
|
||||||
|
oidc:
|
||||||
|
issuer-uri: https://{yourOktaDomain}/oauth2/default
|
||||||
|
registration:
|
||||||
|
oidc:
|
||||||
|
client-id: {clientId}
|
||||||
|
client-secret: {clientSecret}
|
||||||
|
security:
|
||||||
|
```
|
||||||
|
|
||||||
|
Create an OIDC App in Okta to get a `{clientId}` and `{clientSecret}`. To do this, log in to your Okta Developer account and navigate to **Applications** > **Add Application**. Click **Web** and click the **Next** button. Give the app a name you’ll remember, specify `http://localhost:8080` as a Base URI, and `http://localhost:8080/login/oauth2/code/oidc` as a Login Redirect URI. Click **Done**, then Edit and add `http://localhost:8080` as a Logout redirect URI. Copy and paste the client ID and secret into your `application.yml` file.
|
||||||
|
|
||||||
|
Create a `ROLE_ADMIN` and `ROLE_USER` group and add users into them. Modify e2e tests to use this account when running integration tests. You'll need to change credentials in `src/test/javascript/e2e/account/account.spec.ts` and `src/test/javascript/e2e/admin/administration.spec.ts`.
|
||||||
|
|
||||||
|
Navigate to **API** > **Authorization Servers**, click the **Authorization Servers** tab and edit the default one. Click the **Claims** tab and **Add Claim**. Name it "groups", and include it in the ID Token. Set the value type to "Groups" and set the filter to be a Regex of `.*`.
|
||||||
|
|
||||||
|
After making these changes, you should be good to go! If you have any issues, please post them to [Stack Overflow](https://stackoverflow.com/questions/tagged/jhipster). Make sure to tag your question with "jhipster" and "okta".
|
||||||
|
|
||||||
|
### Auth0
|
||||||
|
|
||||||
|
If you'd like to use [Auth0](https://auth0.com/) instead of Keycloak, follow the configuration steps below:
|
||||||
|
|
||||||
|
- Create a free developer account at <https://auth0.com/signup>. After successful sign-up, your account will be associated with a unique domain like `dev-xxx.us.auth0.com`
|
||||||
|
- Create a new application of type `Regular Web Applications`. Switch to the `Settings` tab, and configure your application settings like:
|
||||||
|
- Allowed Callback URLs: `http://localhost:8080/login/oauth2/code/oidc`
|
||||||
|
- Allowed Logout URLs: `http://localhost:8080/`
|
||||||
|
- Navigate to **User Management** > **Roles** and create new roles named `ROLE_ADMIN`, and `ROLE_USER`.
|
||||||
|
- Navigate to **User Management** > **Users** and create a new user account. Click on the **Role** tab to assign roles to the newly created user account.
|
||||||
|
- Navigate to **Auth Pipeline** > **Rules** and create a new Rule. Choose `Empty rule` template. Provide a meaningful name like `JHipster claims` and replace `Script` content with the following and Save.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function (user, context, callback) {
|
||||||
|
user.preferred_username = user.email;
|
||||||
|
const roles = (context.authorization || {}).roles;
|
||||||
|
|
||||||
|
function prepareCustomClaimKey(claim) {
|
||||||
|
return `https://www.jhipster.tech/${claim}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rolesClaim = prepareCustomClaimKey('roles');
|
||||||
|
|
||||||
|
if (context.idToken) {
|
||||||
|
context.idToken[rolesClaim] = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.accessToken) {
|
||||||
|
context.accessToken[rolesClaim] = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, user, context);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- In your `JHipster` application, modify `src/main/resources/config/application.yml` to use your Auth0 application settings:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spring:
|
||||||
|
...
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
client:
|
||||||
|
provider:
|
||||||
|
oidc:
|
||||||
|
# make sure to include the ending slash!
|
||||||
|
issuer-uri: https://{your-auth0-domain}/
|
||||||
|
registration:
|
||||||
|
oidc:
|
||||||
|
client-id: {clientId}
|
||||||
|
client-secret: {clientSecret}
|
||||||
|
scope: openid,profile,email
|
||||||
|
jhipster:
|
||||||
|
...
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
audience:
|
||||||
|
- https://{your-auth0-domain}/api/v2/
|
||||||
|
```
|
||||||
|
|
||||||
|
The build system will install automatically the recommended version of Node and npm.
|
||||||
|
|
||||||
|
We provide a wrapper to launch npm.
|
||||||
|
You will only need to run this command when dependencies change in [package.json](package.json).
|
||||||
|
|
||||||
|
```
|
||||||
|
./npmw install
|
||||||
|
```
|
||||||
|
|
||||||
|
We use npm scripts and [Webpack][] as our build system.
|
||||||
|
|
||||||
|
Run the following commands in two separate terminals to create a blissful development experience where your browser
|
||||||
|
auto-refreshes when files change on your hard drive.
|
||||||
|
|
||||||
|
```
|
||||||
|
./mvnw
|
||||||
|
./npmw start
|
||||||
|
```
|
||||||
|
|
||||||
|
Npm is also used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by
|
||||||
|
specifying a newer version in [package.json](package.json). You can also run `./npmw update` and `./npmw install` to manage dependencies.
|
||||||
|
Add the `help` flag on any command to see how you can use it. For example, `./npmw help update`.
|
||||||
|
|
||||||
|
The `./npmw run` command will list all the scripts available to run for this project.
|
||||||
|
|
||||||
|
### PWA Support
|
||||||
|
|
||||||
|
JHipster ships with PWA (Progressive Web App) support, and it's turned off by default. One of the main components of a PWA is a service worker.
|
||||||
|
|
||||||
|
The service worker initialization code is commented out by default. To enable it, uncomment the following code in `src/main/webapp/index.html`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('./service-worker.js').then(function () {
|
||||||
|
console.log('Service Worker Registered');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: [Workbox](https://developers.google.com/web/tools/workbox/) powers JHipster's service worker. It dynamically generates the `service-worker.js` file.
|
||||||
|
|
||||||
|
### Managing dependencies
|
||||||
|
|
||||||
|
For example, to add [Leaflet][] library as a runtime dependency of your application, you would run following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
./npmw install --save --save-exact leaflet
|
||||||
|
```
|
||||||
|
|
||||||
|
To benefit from TypeScript type definitions from [DefinitelyTyped][] repository in development, you would run following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
./npmw install --save-dev --save-exact @types/leaflet
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you would import the JS and CSS files specified in library's installation instructions so that [Webpack][] knows about them:
|
||||||
|
Note: There are still a few other things remaining to do for Leaflet that we won't detail here.
|
||||||
|
|
||||||
|
For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][].
|
||||||
|
|
||||||
|
## Building for production
|
||||||
|
|
||||||
|
### Packaging as jar
|
||||||
|
|
||||||
|
To build the final jar and optimize the sasiedzi application for production, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
./mvnw -Pprod clean verify
|
||||||
|
```
|
||||||
|
|
||||||
|
This will concatenate and minify the client CSS and JavaScript files. It will also modify `index.html` so it references these new files.
|
||||||
|
To ensure everything worked, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
java -jar target/*.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Then navigate to [http://localhost:8080](http://localhost:8080) in your browser.
|
||||||
|
|
||||||
|
Refer to [Using JHipster in production][] for more details.
|
||||||
|
|
||||||
|
### Packaging as war
|
||||||
|
|
||||||
|
To package your application as a war in order to deploy it to an application server, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
./mvnw -Pprod,war clean verify
|
||||||
|
```
|
||||||
|
|
||||||
|
### JHipster Control Center
|
||||||
|
|
||||||
|
JHipster Control Center can help you manage and control your application(s). You can start a local control center server (accessible on http://localhost:7419) with:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose -f src/main/docker/jhipster-control-center.yml up
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Spring Boot tests
|
||||||
|
|
||||||
|
To launch your application's tests, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
./mvnw verify
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client tests
|
||||||
|
|
||||||
|
Unit tests are run by [Jest][]. They're located in [src/test/javascript/](src/test/javascript/) and can be run with:
|
||||||
|
|
||||||
|
```
|
||||||
|
./npmw test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Others
|
||||||
|
|
||||||
|
### Code quality using Sonar
|
||||||
|
|
||||||
|
Sonar is used to analyse code quality. You can start a local Sonar server (accessible on http://localhost:9001) with:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose -f src/main/docker/sonar.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: we have turned off forced authentication redirect for UI in [src/main/docker/sonar.yml](src/main/docker/sonar.yml) for out of the box experience while trying out SonarQube, for real use cases turn it back on.
|
||||||
|
|
||||||
|
You can run a Sonar analysis with using the [sonar-scanner](https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner) or by using the maven plugin.
|
||||||
|
|
||||||
|
Then, run a Sonar analysis:
|
||||||
|
|
||||||
|
```
|
||||||
|
./mvnw -Pprod clean verify sonar:sonar -Dsonar.login=admin -Dsonar.password=admin
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to re-run the Sonar phase, please be sure to specify at least the `initialize` phase since Sonar properties are loaded from the sonar-project.properties file.
|
||||||
|
|
||||||
|
```
|
||||||
|
./mvnw initialize sonar:sonar -Dsonar.login=admin -Dsonar.password=admin
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, Instead of passing `sonar.password` and `sonar.login` as CLI arguments, these parameters can be configured from [sonar-project.properties](sonar-project.properties) as shown below:
|
||||||
|
|
||||||
|
```
|
||||||
|
sonar.login=admin
|
||||||
|
sonar.password=admin
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information, refer to the [Code quality page][].
|
||||||
|
|
||||||
|
### Docker Compose support
|
||||||
|
|
||||||
|
JHipster generates a number of Docker Compose configuration files in the [src/main/docker/](src/main/docker/) folder to launch required third party services.
|
||||||
|
|
||||||
|
For example, to start required services in Docker containers, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose -f src/main/docker/services.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
To stop and remove the containers, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose -f src/main/docker/services.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
[Spring Docker Compose Integration](https://docs.spring.io/spring-boot/reference/features/dev-services.html) is enable by default. It's possible to disable it in application.yml:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spring:
|
||||||
|
...
|
||||||
|
docker:
|
||||||
|
compose:
|
||||||
|
enabled: false
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also fully dockerize your application and all the services that it depends on.
|
||||||
|
To achieve this, first build a Docker image of your app by running:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run java:docker
|
||||||
|
```
|
||||||
|
|
||||||
|
Or build a arm64 Docker image when using an arm64 processor os like MacOS with M1 processor family running:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run java:docker:arm64
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose -f src/main/docker/app.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the Docker Compose sub-generator (`jhipster docker-compose`), which is able to generate Docker configurations for one or several JHipster applications.
|
||||||
|
|
||||||
|
## Continuous Integration (optional)
|
||||||
|
|
||||||
|
To configure CI for your project, run the ci-cd sub-generator (`jhipster ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information.
|
||||||
|
|
||||||
|
[JHipster Homepage and latest documentation]: https://www.jhipster.tech
|
||||||
|
[JHipster 8.7.2 archive]: https://www.jhipster.tech/documentation-archive/v8.7.2
|
||||||
|
[Using JHipster in development]: https://www.jhipster.tech/documentation-archive/v8.7.2/development/
|
||||||
|
[Using Docker and Docker-Compose]: https://www.jhipster.tech/documentation-archive/v8.7.2/docker-compose
|
||||||
|
[Using JHipster in production]: https://www.jhipster.tech/documentation-archive/v8.7.2/production/
|
||||||
|
[Running tests page]: https://www.jhipster.tech/documentation-archive/v8.7.2/running-tests/
|
||||||
|
[Code quality page]: https://www.jhipster.tech/documentation-archive/v8.7.2/code-quality/
|
||||||
|
[Setting up Continuous Integration]: https://www.jhipster.tech/documentation-archive/v8.7.2/setting-up-ci/
|
||||||
|
[Node.js]: https://nodejs.org/
|
||||||
|
[NPM]: https://www.npmjs.com/
|
||||||
|
[Webpack]: https://webpack.github.io/
|
||||||
|
[BrowserSync]: https://www.browsersync.io/
|
||||||
|
[Jest]: https://facebook.github.io/jest/
|
||||||
|
[Leaflet]: https://leafletjs.com/
|
||||||
|
[DefinitelyTyped]: https://definitelytyped.org/
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||||
|
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||||
|
<module name="Checker">
|
||||||
|
|
||||||
|
<!-- Configure checker to use UTF-8 encoding -->
|
||||||
|
<property name="charset" value="UTF-8"/>
|
||||||
|
<!-- Configure checker to run on files with these extensions -->
|
||||||
|
<property name="fileExtensions" value=""/>
|
||||||
|
<!-- For detailed checkstyle configuration, see https://github.com/spring-io/nohttp/tree/master/nohttp-checkstyle -->
|
||||||
|
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck">
|
||||||
|
<property name="allowlist" value="^\Qhttp://maven.apache.org/POM/4.0.0 ^\Qhttp://www.w3.org/2001/XMLSchema-instance"/>
|
||||||
|
</module>
|
||||||
|
<!-- Allow suppression with comments
|
||||||
|
// CHECKSTYLE:OFF
|
||||||
|
... ignored content ...
|
||||||
|
// CHECKSTYLE:ON
|
||||||
|
-->
|
||||||
|
<module name="SuppressWithPlainTextCommentFilter"/>
|
||||||
|
</module>
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import globals from 'globals';
|
||||||
|
import prettier from 'eslint-plugin-prettier/recommended';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import vue from 'eslint-plugin-vue';
|
||||||
|
// jhipster-needle-eslint-add-import - JHipster will add additional import here
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ ignores: ['src/main/docker/'] },
|
||||||
|
{ ignores: ['target/classes/static/', 'target/'] },
|
||||||
|
js.configs.recommended,
|
||||||
|
...tseslint.configs.recommended.map(config =>
|
||||||
|
config.name === 'typescript-eslint/base' ? config : { ...config, files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'] },
|
||||||
|
),
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,cjs,mjs}'],
|
||||||
|
rules: {
|
||||||
|
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...vue.configs['flat/recommended'],
|
||||||
|
{
|
||||||
|
files: ['**/*.vue'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: { parser: '@typescript-eslint/parser' },
|
||||||
|
globals: { ...globals.browser },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['src/main/webapp/**/*.vue', 'src/main/webapp/**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.browser },
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-imports': 'error',
|
||||||
|
'vue/no-v-text-v-html-on-component': ['error', { allow: ['router-link', 'b-alert', 'b-badge', 'b-button', 'b-link'] }],
|
||||||
|
'vue/no-reserved-component-names': 'off',
|
||||||
|
'vue/attributes-order': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['src/main/webapp/**/*.spec.ts'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// jhipster-needle-eslint-add-config - JHipster will add additional config here
|
||||||
|
prettier,
|
||||||
|
);
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Apache Maven Wrapper startup batch script, version 3.3.1
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||||
|
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
|
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
|
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -euf
|
||||||
|
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||||
|
|
||||||
|
# OS specific support.
|
||||||
|
native_path() { printf %s\\n "$1"; }
|
||||||
|
case "$(uname)" in
|
||||||
|
CYGWIN* | MINGW*)
|
||||||
|
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||||
|
native_path() { cygpath --path --windows "$1"; }
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# set JAVACMD and JAVACCMD
|
||||||
|
set_java_home() {
|
||||||
|
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||||
|
if [ -n "${JAVA_HOME-}" ]; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||||
|
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||||
|
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v java
|
||||||
|
)" || :
|
||||||
|
JAVACCMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v javac
|
||||||
|
)" || :
|
||||||
|
|
||||||
|
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||||
|
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# hash string like Java String::hashCode
|
||||||
|
hash_string() {
|
||||||
|
str="${1:-}" h=0
|
||||||
|
while [ -n "$str" ]; do
|
||||||
|
char="${str%"${str#?}"}"
|
||||||
|
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||||
|
str="${str#?}"
|
||||||
|
done
|
||||||
|
printf %x\\n $h
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose() { :; }
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf %s\\n "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
|
while IFS="=" read -r key value; do
|
||||||
|
case "${key-}" in
|
||||||
|
distributionUrl) distributionUrl="${value-}" ;;
|
||||||
|
distributionSha256Sum) distributionSha256Sum="${value-}" ;;
|
||||||
|
esac
|
||||||
|
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
|
||||||
|
case "${distributionUrl##*/}" in
|
||||||
|
maven-mvnd-*bin.*)
|
||||||
|
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||||
|
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||||
|
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||||
|
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||||
|
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||||
|
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||||
|
*)
|
||||||
|
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||||
|
distributionPlatform=linux-amd64
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||||
|
;;
|
||||||
|
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||||
|
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||||
|
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||||
|
MAVEN_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||||
|
|
||||||
|
exec_maven() {
|
||||||
|
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||||
|
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d "$MAVEN_HOME" ]; then
|
||||||
|
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
exec_maven "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${distributionUrl-}" in
|
||||||
|
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||||
|
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||||
|
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||||
|
trap clean HUP INT TERM EXIT
|
||||||
|
else
|
||||||
|
die "cannot create temp dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
verbose "Downloading from: $distributionUrl"
|
||||||
|
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
# select .zip or .tar.gz
|
||||||
|
if ! command -v unzip >/dev/null; then
|
||||||
|
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# verbose opt
|
||||||
|
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||||
|
|
||||||
|
# normalize http auth
|
||||||
|
case "${MVNW_PASSWORD:+has-password}" in
|
||||||
|
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||||
|
verbose "Found wget ... using wget"
|
||||||
|
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||||
|
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||||
|
verbose "Found curl ... using curl"
|
||||||
|
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||||
|
elif set_java_home; then
|
||||||
|
verbose "Falling back to use Java to download"
|
||||||
|
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||||
|
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
cat >"$javaSource" <<-END
|
||||||
|
public class Downloader extends java.net.Authenticator
|
||||||
|
{
|
||||||
|
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||||
|
{
|
||||||
|
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||||
|
}
|
||||||
|
public static void main( String[] args ) throws Exception
|
||||||
|
{
|
||||||
|
setDefault( new Downloader() );
|
||||||
|
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END
|
||||||
|
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||||
|
verbose " - Compiling Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||||
|
verbose " - Running Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
if [ -n "${distributionSha256Sum-}" ]; then
|
||||||
|
distributionSha256Result=false
|
||||||
|
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||||
|
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||||
|
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
elif command -v sha256sum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
|
fi
|
||||||
|
elif command -v shasum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||||
|
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ $distributionSha256Result = false ]; then
|
||||||
|
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||||
|
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
if command -v unzip >/dev/null; then
|
||||||
|
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||||
|
else
|
||||||
|
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||||
|
fi
|
||||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
|
||||||
|
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||||
|
|
||||||
|
clean || :
|
||||||
|
exec_maven "$@"
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<# : batch portion
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Apache Maven Wrapper startup batch script, version 3.3.1
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||||
|
@SET __MVNW_CMD__=
|
||||||
|
@SET __MVNW_ERROR__=
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||||
|
@SET PSModulePath=
|
||||||
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||||
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||||
|
)
|
||||||
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=
|
||||||
|
@SET __MVNW_ARG0_NAME__=
|
||||||
|
@SET MVNW_USERNAME=
|
||||||
|
@SET MVNW_PASSWORD=
|
||||||
|
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||||
|
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||||
|
@GOTO :EOF
|
||||||
|
: end batch / begin powershell #>
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
if ($env:MVNW_VERBOSE -eq "true") {
|
||||||
|
$VerbosePreference = "Continue"
|
||||||
|
}
|
||||||
|
|
||||||
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||||
|
if (!$distributionUrl) {
|
||||||
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||||
|
"maven-mvnd-*" {
|
||||||
|
$USE_MVND = $true
|
||||||
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||||
|
$MVN_CMD = "mvnd.cmd"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
$USE_MVND = $false
|
||||||
|
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
if ($env:MVNW_REPOURL) {
|
||||||
|
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||||
|
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||||
|
}
|
||||||
|
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||||
|
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||||
|
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||||
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||||
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||||
|
|
||||||
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||||
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
exit $?
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||||
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||||
|
}
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||||
|
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||||
|
trap {
|
||||||
|
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
Write-Verbose "Downloading from: $distributionUrl"
|
||||||
|
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
$webclient = New-Object System.Net.WebClient
|
||||||
|
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||||
|
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||||
|
}
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||||
|
if ($distributionSha256Sum) {
|
||||||
|
if ($USE_MVND) {
|
||||||
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||||
|
}
|
||||||
|
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||||
|
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||||
|
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||||
|
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||||
|
try {
|
||||||
|
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||||
|
} catch {
|
||||||
|
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||||
|
Write-Error "fail to move MAVEN_HOME"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
basedir=$(dirname "$0")
|
||||||
|
|
||||||
|
if [ -f "$basedir/mvnw" ]; then
|
||||||
|
bindir="$basedir/target/node"
|
||||||
|
repodir="$basedir/target/node/node_modules"
|
||||||
|
installCommand="$basedir/mvnw --batch-mode -ntp -Pwebapp frontend:install-node-and-npm@install-node-and-npm"
|
||||||
|
elif [ -f "$basedir/gradlew" ]; then
|
||||||
|
bindir="$basedir/build/node/bin"
|
||||||
|
repodir="$basedir/build/node/lib/node_modules"
|
||||||
|
installCommand="$basedir/gradlew npmSetup"
|
||||||
|
else
|
||||||
|
echo "Using npm installed globally"
|
||||||
|
exec npm "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NPM_EXE="$repodir/npm/bin/npm-cli.js"
|
||||||
|
NODE_EXE="$bindir/node"
|
||||||
|
|
||||||
|
if [ ! -x "$NPM_EXE" ] || [ ! -x "$NODE_EXE" ]; then
|
||||||
|
$installCommand || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -x "$NODE_EXE" ]; then
|
||||||
|
echo "Using node installed locally $($NODE_EXE --version)"
|
||||||
|
PATH="$bindir:$PATH"
|
||||||
|
else
|
||||||
|
NODE_EXE='node'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$NPM_EXE" ]; then
|
||||||
|
echo "Local npm not found, using npm installed globally"
|
||||||
|
npm "$@"
|
||||||
|
else
|
||||||
|
echo "Using npm installed locally $($NODE_EXE $NPM_EXE --version)"
|
||||||
|
$NODE_EXE $NPM_EXE "$@"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
set NPMW_DIR=%~dp0
|
||||||
|
|
||||||
|
if exist "%NPMW_DIR%mvnw.cmd" (
|
||||||
|
set NODE_EXE=^"^"
|
||||||
|
set NODE_PATH=%NPMW_DIR%target\node\
|
||||||
|
set NPM_EXE=^"%NPMW_DIR%target\node\npm.cmd^"
|
||||||
|
set INSTALL_NPM_COMMAND=^"%NPMW_DIR%mvnw.cmd^" -Pwebapp frontend:install-node-and-npm@install-node-and-npm
|
||||||
|
) else (
|
||||||
|
set NODE_EXE=^"%NPMW_DIR%build\node\bin\node.exe^"
|
||||||
|
set NODE_PATH=%NPMW_DIR%build\node\bin\
|
||||||
|
set NPM_EXE=^"%NPMW_DIR%build\node\lib\node_modules\npm\bin\npm-cli.js^"
|
||||||
|
set INSTALL_NPM_COMMAND=^"%NPMW_DIR%gradlew.bat^" npmSetup
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exist %NPM_EXE% (
|
||||||
|
call %INSTALL_NPM_COMMAND%
|
||||||
|
)
|
||||||
|
|
||||||
|
if exist %NODE_EXE% (
|
||||||
|
Rem execute local npm with local node, whilst adding local node location to the PATH for this CMD session
|
||||||
|
endlocal & echo "%PATH%"|find /i "%NODE_PATH%;">nul || set "PATH=%NODE_PATH%;%PATH%" & call %NODE_EXE% %NPM_EXE% %*
|
||||||
|
) else if exist %NPM_EXE% (
|
||||||
|
Rem execute local npm, whilst adding local npm location to the PATH for this CMD session
|
||||||
|
endlocal & echo "%PATH%"|find /i "%NODE_PATH%;">nul || set "PATH=%NODE_PATH%;%PATH%" & call %NPM_EXE% %*
|
||||||
|
) else (
|
||||||
|
call npm %*
|
||||||
|
)
|
||||||
Generated
+13319
File diff suppressed because it is too large
Load Diff
+143
@@ -0,0 +1,143 @@
|
|||||||
|
{
|
||||||
|
"name": "sasiedzi",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "Description for Sasiedzi",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"scripts": {
|
||||||
|
"app:start": "./mvnw",
|
||||||
|
"app:up": "docker compose -f src/main/docker/app.yml up --wait",
|
||||||
|
"backend:build-cache": "./mvnw dependency:go-offline -ntp",
|
||||||
|
"backend:debug": "./mvnw -Dspring-boot.run.jvmArguments=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000\"",
|
||||||
|
"backend:doc:test": "./mvnw -ntp javadoc:javadoc --batch-mode",
|
||||||
|
"backend:info": "./mvnw --version",
|
||||||
|
"backend:nohttp:test": "./mvnw -ntp checkstyle:check --batch-mode",
|
||||||
|
"backend:start": "./mvnw -Dskip.installnodenpm -Dskip.npm",
|
||||||
|
"backend:unit:test": "./mvnw -ntp -Dskip.installnodenpm -Dskip.npm verify --batch-mode -Dlogging.level.ROOT=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.com.sasiedzi.event=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF",
|
||||||
|
"build": "npm run webapp:prod --",
|
||||||
|
"build-watch": "concurrently 'npm run webapp:build:dev -- --watch' npm:backend:start",
|
||||||
|
"ci:backend:test": "npm run backend:info && npm run backend:doc:test && npm run backend:nohttp:test && npm run backend:unit:test -- -P$npm_package_config_default_environment",
|
||||||
|
"ci:e2e:package": "npm run java:$npm_package_config_packaging:$npm_package_config_default_environment -- -Pe2e -Denforcer.skip=true",
|
||||||
|
"ci:e2e:prepare": "npm run ci:e2e:prepare:docker",
|
||||||
|
"ci:e2e:prepare:docker": "npm run services:up --if-present && docker ps -a",
|
||||||
|
"preci:e2e:server:start": "npm run services:db:await --if-present && npm run services:others:await --if-present",
|
||||||
|
"ci:e2e:server:start": "java -jar target/e2e.$npm_package_config_packaging --spring.profiles.active=e2e,$npm_package_config_default_environment -Dlogging.level.ROOT=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.com.sasiedzi.event=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF --logging.level.org.springframework.web=ERROR",
|
||||||
|
"ci:e2e:teardown": "npm run ci:e2e:teardown:docker --if-present",
|
||||||
|
"ci:e2e:teardown:docker": "docker compose -f src/main/docker/services.yml down -v && docker ps -a",
|
||||||
|
"ci:frontend:build": "npm run webapp:build:$npm_package_config_default_environment",
|
||||||
|
"ci:frontend:test": "npm run ci:frontend:build && npm test",
|
||||||
|
"ci:server:await": "echo \"Waiting for server at port $npm_package_config_backend_port to start\" && wait-on -t 360000 http-get://127.0.0.1:$npm_package_config_backend_port/management/health && echo \"Server at port $npm_package_config_backend_port started\"",
|
||||||
|
"clean-www": "rimraf target/classes/static/",
|
||||||
|
"cleanup": "rimraf target/",
|
||||||
|
"docker:db:down": "docker compose -f src/main/docker/postgresql.yml down -v",
|
||||||
|
"docker:db:up": "docker compose -f src/main/docker/postgresql.yml up --wait",
|
||||||
|
"docker:keycloak:down": "docker compose -f src/main/docker/keycloak.yml down -v",
|
||||||
|
"docker:keycloak:up": "docker compose -f src/main/docker/keycloak.yml up --wait",
|
||||||
|
"java:docker": "./mvnw -ntp verify -DskipTests -Pprod jib:dockerBuild",
|
||||||
|
"java:docker:arm64": "npm run java:docker -- -Djib-maven-plugin.architecture=arm64",
|
||||||
|
"java:docker:dev": "npm run java:docker -- -Pdev,webapp",
|
||||||
|
"java:docker:prod": "npm run java:docker -- -Pprod",
|
||||||
|
"java:jar": "./mvnw -ntp verify -DskipTests --batch-mode",
|
||||||
|
"java:jar:dev": "npm run java:jar -- -Pdev,webapp",
|
||||||
|
"java:jar:prod": "npm run java:jar -- -Pprod",
|
||||||
|
"java:war": "./mvnw -ntp verify -DskipTests --batch-mode -Pwar",
|
||||||
|
"java:war:dev": "npm run java:war -- -Pdev,webapp",
|
||||||
|
"java:war:prod": "npm run java:war -- -Pprod",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:fix": "eslint . --fix",
|
||||||
|
"prepare": "husky",
|
||||||
|
"prettier:check": "prettier --check \"{,src/**/,.blueprint/**/}*.{md,json,yml,js,cjs,mjs,ts,cts,mts,java,html,vue,css,scss}\"",
|
||||||
|
"prettier:format": "prettier --write \"{,src/**/,.blueprint/**/}*.{md,json,yml,js,cjs,mjs,ts,cts,mts,java,html,vue,css,scss}\"",
|
||||||
|
"serve": "npm run start --",
|
||||||
|
"services:up": "docker compose -f src/main/docker/services.yml up --wait",
|
||||||
|
"start": "npm run webapp:dev --",
|
||||||
|
"start-tls": "npm run webapp:dev -- --env.tls",
|
||||||
|
"pretest": "npm run lint",
|
||||||
|
"test": "npm run vitest-run --",
|
||||||
|
"test:watch": "npm run vitest",
|
||||||
|
"vite-build": "vite build",
|
||||||
|
"vite-serve": "vite",
|
||||||
|
"vitest": "vitest",
|
||||||
|
"vitest-run": "vitest --run --coverage",
|
||||||
|
"watch": "concurrently npm:start npm:backend:start",
|
||||||
|
"webapp:build": "npm run clean-www && npm run webapp:build:dev --",
|
||||||
|
"webapp:build:dev": "npm run vite-build",
|
||||||
|
"webapp:build:prod": "npm run vite-build",
|
||||||
|
"webapp:dev": "npm run vite-serve",
|
||||||
|
"webapp:prod": "npm run clean-www && npm run webapp:build:prod --",
|
||||||
|
"webapp:serve": "npm run vite-serve",
|
||||||
|
"webapp:test": "npm run test --"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"backend_port": 8080,
|
||||||
|
"default_environment": "prod",
|
||||||
|
"packaging": "jar"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not ie <= 8"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "6.6.0",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "6.6.0",
|
||||||
|
"@fortawesome/vue-fontawesome": "3.0.8",
|
||||||
|
"@vue/compat": "3.5.12",
|
||||||
|
"@vuelidate/core": "2.0.3",
|
||||||
|
"@vuelidate/validators": "2.0.4",
|
||||||
|
"@vueuse/core": "11.1.0",
|
||||||
|
"axios": "1.7.7",
|
||||||
|
"bootstrap": "4.6.2",
|
||||||
|
"bootstrap-vue": "2.23.1",
|
||||||
|
"dayjs": "1.11.13",
|
||||||
|
"pinia": "2.2.4",
|
||||||
|
"vue": "3.5.12",
|
||||||
|
"vue-router": "4.4.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "9.13.0",
|
||||||
|
"@pinia/testing": "0.1.6",
|
||||||
|
"@tsconfig/node18": "18.2.4",
|
||||||
|
"@types/node": "20.11.25",
|
||||||
|
"@types/sinon": "17.0.3",
|
||||||
|
"@vitejs/plugin-vue": "5.1.4",
|
||||||
|
"@vitest/coverage-v8": "2.1.3",
|
||||||
|
"@vue/test-utils": "2.4.6",
|
||||||
|
"@vue/tsconfig": "0.5.1",
|
||||||
|
"autoprefixer": "10.4.20",
|
||||||
|
"axios-mock-adapter": "2.1.0",
|
||||||
|
"concurrently": "9.0.1",
|
||||||
|
"eslint": "9.13.0",
|
||||||
|
"eslint-config-prettier": "9.1.0",
|
||||||
|
"eslint-plugin-prettier": "5.2.1",
|
||||||
|
"eslint-plugin-vue": "9.29.1",
|
||||||
|
"flush-promises": "1.0.2",
|
||||||
|
"generator-jhipster": "8.7.2",
|
||||||
|
"happy-dom": "14.12.3",
|
||||||
|
"husky": "9.1.6",
|
||||||
|
"lint-staged": "15.2.10",
|
||||||
|
"numeral": "2.0.6",
|
||||||
|
"postcss-import": "16.1.0",
|
||||||
|
"postcss-url": "10.1.3",
|
||||||
|
"prettier": "3.3.3",
|
||||||
|
"prettier-plugin-java": "2.6.5",
|
||||||
|
"prettier-plugin-packagejson": "2.5.3",
|
||||||
|
"rimraf": "5.0.8",
|
||||||
|
"sass": "1.64.2",
|
||||||
|
"sinon": "19.0.2",
|
||||||
|
"swagger-ui-dist": "5.17.14",
|
||||||
|
"typescript": "5.6.3",
|
||||||
|
"typescript-eslint": "8.11.0",
|
||||||
|
"vite": "5.4.10",
|
||||||
|
"vite-plugin-static-copy": "2.0.0",
|
||||||
|
"vitest": "2.1.3",
|
||||||
|
"vitest-sonar-reporter": "2.0.0",
|
||||||
|
"wait-on": "8.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.18.0"
|
||||||
|
},
|
||||||
|
"cacheDirectories": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
sonar.projectKey = sasiedzi
|
||||||
|
sonar.projectName = sasiedzi generated by jhipster
|
||||||
|
|
||||||
|
# Typescript tests files must be inside sources and tests, otherwise `INFO: Test execution data ignored for 80 unknown files, including:`
|
||||||
|
# is shown.
|
||||||
|
sonar.sources = src
|
||||||
|
sonar.tests = src
|
||||||
|
sonar.host.url = http://localhost:9001
|
||||||
|
|
||||||
|
sonar.test.inclusions = src/test/**/*.*
|
||||||
|
sonar.coverage.jacoco.xmlReportPaths = target/site/**/jacoco*.xml
|
||||||
|
sonar.java.codeCoveragePlugin = jacoco
|
||||||
|
sonar.junit.reportPaths = target/surefire-reports,target/failsafe-reports
|
||||||
|
sonar.testExecutionReportPaths = target/test-results/jest/TESTS-results-sonar.xml
|
||||||
|
sonar.javascript.lcov.reportPaths = target/test-results/lcov.info
|
||||||
|
|
||||||
|
sonar.sourceEncoding = UTF-8
|
||||||
|
sonar.exclusions = src/main/webapp/content/**/*.*, src/main/webapp/i18n/*.js, target/classes/static/**/*.*
|
||||||
|
|
||||||
|
sonar.issue.ignore.multicriteria = S6437,S125,S3437,S4684,S5145,UndocumentedApi
|
||||||
|
|
||||||
|
# Rule https://rules.sonarsource.com/java/RSPEC-6437 is ignored, hardcoded passwords are provided for development purposes
|
||||||
|
sonar.issue.ignore.multicriteria.S6437.resourceKey = src/main/resources/config/*
|
||||||
|
sonar.issue.ignore.multicriteria.S6437.ruleKey = java:S6437
|
||||||
|
# Rule https://rules.sonarsource.com/java/RSPEC-3437 is ignored, as a JPA-managed field cannot be transient
|
||||||
|
sonar.issue.ignore.multicriteria.S3437.resourceKey = src/main/java/**/*
|
||||||
|
sonar.issue.ignore.multicriteria.S3437.ruleKey = squid:S3437
|
||||||
|
# Rule https://rules.sonarsource.com/java/RSPEC-4684
|
||||||
|
sonar.issue.ignore.multicriteria.S4684.resourceKey = src/main/java/**/*
|
||||||
|
sonar.issue.ignore.multicriteria.S4684.ruleKey = java:S4684
|
||||||
|
# Rule https://rules.sonarsource.com/java/RSPEC-5145 log filter is applied
|
||||||
|
sonar.issue.ignore.multicriteria.S5145.resourceKey = src/main/java/**/*
|
||||||
|
sonar.issue.ignore.multicriteria.S5145.ruleKey = javasecurity:S5145
|
||||||
|
# Rule https://rules.sonarsource.com/java/RSPEC-1176 is ignored, as we want to follow "clean code" guidelines and classes, methods and
|
||||||
|
# arguments names should be self-explanatory
|
||||||
|
sonar.issue.ignore.multicriteria.UndocumentedApi.resourceKey = src/main/java/**/*
|
||||||
|
sonar.issue.ignore.multicriteria.UndocumentedApi.ruleKey = squid:UndocumentedApi
|
||||||
|
# Rule https://rules.sonarsource.com/xml/RSPEC-125
|
||||||
|
sonar.issue.ignore.multicriteria.S125.resourceKey = src/main/resources/logback-spring.xml
|
||||||
|
sonar.issue.ignore.multicriteria.S125.ruleKey = xml:S125
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
|
||||||
|
name: sasiedzi
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: sasiedzi
|
||||||
|
environment:
|
||||||
|
- _JAVA_OPTIONS=-Xmx512m -Xms256m
|
||||||
|
- SPRING_PROFILES_ACTIVE=prod,api-docs
|
||||||
|
- MANAGEMENT_PROMETHEUS_METRICS_EXPORT_ENABLED=true
|
||||||
|
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgresql:5432/sasiedzi
|
||||||
|
- SPRING_LIQUIBASE_URL=jdbc:postgresql://postgresql:5432/sasiedzi
|
||||||
|
- SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=http://keycloak:9080/realms/jhipster
|
||||||
|
- SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID=web_app
|
||||||
|
- SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET=web_app
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:8080:8080
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- curl
|
||||||
|
- -f
|
||||||
|
- http://localhost:8080/management/health
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 40
|
||||||
|
depends_on:
|
||||||
|
postgresql:
|
||||||
|
condition: service_healthy
|
||||||
|
keycloak:
|
||||||
|
condition: service_healthy
|
||||||
|
postgresql:
|
||||||
|
extends:
|
||||||
|
file: ./postgresql.yml
|
||||||
|
service: postgresql
|
||||||
|
keycloak:
|
||||||
|
extends:
|
||||||
|
file: ./keycloak.yml
|
||||||
|
service: keycloak
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: 'Prometheus'
|
||||||
|
orgId: 1
|
||||||
|
folder: ''
|
||||||
|
type: file
|
||||||
|
disableDeletion: false
|
||||||
|
editable: true
|
||||||
|
options:
|
||||||
|
path: /etc/grafana/provisioning/dashboards
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
# list of datasources that should be deleted from the database
|
||||||
|
deleteDatasources:
|
||||||
|
- name: Prometheus
|
||||||
|
orgId: 1
|
||||||
|
|
||||||
|
# list of datasources to insert/update depending
|
||||||
|
# whats available in the database
|
||||||
|
datasources:
|
||||||
|
# <string, required> name of the datasource. Required
|
||||||
|
- name: Prometheus
|
||||||
|
# <string, required> datasource type. Required
|
||||||
|
type: prometheus
|
||||||
|
# <string, required> access mode. direct or proxy. Required
|
||||||
|
access: proxy
|
||||||
|
# <int> org id. will default to orgId 1 if not specified
|
||||||
|
orgId: 1
|
||||||
|
# <string> url
|
||||||
|
# On MacOS, replace localhost by host.docker.internal
|
||||||
|
url: http://localhost:9090
|
||||||
|
# <string> database password, if used
|
||||||
|
password:
|
||||||
|
# <string> database user, if used
|
||||||
|
user:
|
||||||
|
# <string> database name, if used
|
||||||
|
database:
|
||||||
|
# <bool> enable/disable basic auth
|
||||||
|
basicAuth: false
|
||||||
|
# <string> basic auth username
|
||||||
|
basicAuthUser: admin
|
||||||
|
# <string> basic auth password
|
||||||
|
basicAuthPassword: admin
|
||||||
|
# <bool> enable/disable with credentials headers
|
||||||
|
withCredentials:
|
||||||
|
# <bool> mark as default datasource. Max one per org
|
||||||
|
isDefault: true
|
||||||
|
# <map> fields that will be converted to json and stored in json_data
|
||||||
|
jsonData:
|
||||||
|
graphiteVersion: '1.1'
|
||||||
|
tlsAuth: false
|
||||||
|
tlsAuthWithCACert: false
|
||||||
|
# <string> json object of data that will be encrypted.
|
||||||
|
secureJsonData:
|
||||||
|
tlsCACert: '...'
|
||||||
|
tlsClientCert: '...'
|
||||||
|
tlsClientKey: '...'
|
||||||
|
version: 1
|
||||||
|
# <bool> allow users to edit datasources from the UI.
|
||||||
|
editable: true
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
## How to use JHCC docker compose
|
||||||
|
# To allow JHCC to reach JHipster application from a docker container note that we set the host as host.docker.internal
|
||||||
|
# To reach the application from a browser, you need to add '127.0.0.1 host.docker.internal' to your hosts file.
|
||||||
|
### Discovery mode
|
||||||
|
# JHCC support 3 kinds of discovery mode: Consul, Eureka and static
|
||||||
|
# In order to use one, please set SPRING_PROFILES_ACTIVE to one (and only one) of this values: consul,eureka,static
|
||||||
|
### Discovery properties
|
||||||
|
# According to the discovery mode choose as Spring profile, you have to set the right properties
|
||||||
|
# please note that current properties are set to run JHCC with default values, personalize them if needed
|
||||||
|
# and remove those from other modes. You can only have one mode active.
|
||||||
|
#### Eureka
|
||||||
|
# - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:admin@host.docker.internal:8761/eureka/
|
||||||
|
#### Consul
|
||||||
|
# - SPRING_CLOUD_CONSUL_HOST=host.docker.internal
|
||||||
|
# - SPRING_CLOUD_CONSUL_PORT=8500
|
||||||
|
#### Static
|
||||||
|
# Add instances to "MyApp"
|
||||||
|
# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYAPP_0_URI=http://host.docker.internal:8081
|
||||||
|
# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYAPP_1_URI=http://host.docker.internal:8082
|
||||||
|
# Or add a new application named MyNewApp
|
||||||
|
# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYNEWAPP_0_URI=http://host.docker.internal:8080
|
||||||
|
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
|
||||||
|
|
||||||
|
#### IMPORTANT
|
||||||
|
# If you choose Consul or Eureka mode:
|
||||||
|
# Do not forget to remove the prefix "127.0.0.1" in front of their port in order to expose them.
|
||||||
|
# This is required because JHCC need to communicate with Consul or Eureka.
|
||||||
|
# - In Consul mode, the ports are in the consul.yml file.
|
||||||
|
# - In Eureka mode, the ports are in the jhipster-registry.yml file.
|
||||||
|
|
||||||
|
name: sasiedzi
|
||||||
|
services:
|
||||||
|
jhipster-control-center:
|
||||||
|
image: 'jhipster/jhipster-control-center:v0.5.0'
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
# Patch /etc/hosts to support resolving host.docker.internal to the internal IP address used by the host in all OSes
|
||||||
|
- echo "`ip route | grep default | cut -d ' ' -f3` host.docker.internal" | tee -a /etc/hosts > /dev/null && java -jar /jhipster-control-center.jar
|
||||||
|
environment:
|
||||||
|
- _JAVA_OPTIONS=-Xmx512m -Xms256m
|
||||||
|
- SPRING_PROFILES_ACTIVE=prod,api-docs,static,oauth2
|
||||||
|
# For keycloak to work, you need to add '127.0.0.1 keycloak' to your hosts file
|
||||||
|
- SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=http://keycloak:9080/realms/jhipster
|
||||||
|
- SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID=jhipster-control-center
|
||||||
|
- SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET=jhipster-control-center
|
||||||
|
- SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_SASIEDZI_0_URI=http://host.docker.internal:8080
|
||||||
|
- LOGGING_FILE_NAME=/tmp/jhipster-control-center.log
|
||||||
|
# If you want to expose these ports outside your dev PC,
|
||||||
|
# remove the "127.0.0.1:" prefix
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:7419:7419
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP}
|
||||||
|
|
||||||
|
# usage: file_env VAR [DEFAULT]
|
||||||
|
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
|
||||||
|
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
|
||||||
|
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
|
||||||
|
file_env() {
|
||||||
|
local var="$1"
|
||||||
|
local fileVar="${var}_FILE"
|
||||||
|
local def="${2:-}"
|
||||||
|
if [[ ${!var:-} && ${!fileVar:-} ]]; then
|
||||||
|
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
local val="$def"
|
||||||
|
if [[ ${!var:-} ]]; then
|
||||||
|
val="${!var}"
|
||||||
|
elif [[ ${!fileVar:-} ]]; then
|
||||||
|
val="$(< "${!fileVar}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $val ]]; then
|
||||||
|
export "$var"="$val"
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset "$fileVar"
|
||||||
|
}
|
||||||
|
|
||||||
|
file_env 'SPRING_DATASOURCE_URL'
|
||||||
|
file_env 'SPRING_DATASOURCE_USERNAME'
|
||||||
|
file_env 'SPRING_DATASOURCE_PASSWORD'
|
||||||
|
file_env 'SPRING_LIQUIBASE_URL'
|
||||||
|
file_env 'SPRING_LIQUIBASE_USER'
|
||||||
|
file_env 'SPRING_LIQUIBASE_PASSWORD'
|
||||||
|
file_env 'JHIPSTER_REGISTRY_PASSWORD'
|
||||||
|
|
||||||
|
exec java ${JAVA_OPTS} -noverify -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* "com.sasiedzi.event.SasiedziApp" "$@"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
|
||||||
|
name: sasiedzi
|
||||||
|
services:
|
||||||
|
keycloak:
|
||||||
|
image: quay.io/keycloak/keycloak:25.0.1
|
||||||
|
command: 'start-dev --import-realm'
|
||||||
|
volumes:
|
||||||
|
- ./realm-config:/opt/keycloak/data/import
|
||||||
|
- ./realm-config/keycloak-health-check.sh:/opt/keycloak/health-check.sh
|
||||||
|
environment:
|
||||||
|
- KC_DB=dev-file
|
||||||
|
- KEYCLOAK_ADMIN=admin
|
||||||
|
- KEYCLOAK_ADMIN_PASSWORD=admin
|
||||||
|
- KC_FEATURES=scripts
|
||||||
|
- KC_HTTP_PORT=9080
|
||||||
|
- KC_HTTPS_PORT=9443
|
||||||
|
- KC_HEALTH_ENABLED=true
|
||||||
|
- KC_HTTP_MANAGEMENT_PORT=9990
|
||||||
|
# If you want to expose these ports outside your dev PC,
|
||||||
|
# remove the "127.0.0.1:" prefix
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:9080:9080
|
||||||
|
- 127.0.0.1:9443:9443
|
||||||
|
healthcheck:
|
||||||
|
test: 'bash /opt/keycloak/health-check.sh'
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
# Increased retries due to slow Keycloak startup in GitHub Actions using MacOS
|
||||||
|
retries: 50
|
||||||
|
start_period: 10s
|
||||||
|
labels:
|
||||||
|
org.springframework.boot.ignore: true
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
|
||||||
|
name: sasiedzi
|
||||||
|
services:
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:v2.55.0
|
||||||
|
volumes:
|
||||||
|
- ./prometheus/:/etc/prometheus/
|
||||||
|
command:
|
||||||
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||||
|
# If you want to expose these ports outside your dev PC,
|
||||||
|
# remove the "127.0.0.1:" prefix
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:9090:9090
|
||||||
|
# On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and
|
||||||
|
# grafana/provisioning/datasources/datasource.yml
|
||||||
|
network_mode: 'host' # to test locally running service
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:11.3.0
|
||||||
|
volumes:
|
||||||
|
- ./grafana/provisioning/:/etc/grafana/provisioning/
|
||||||
|
environment:
|
||||||
|
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||||
|
- GF_USERS_ALLOW_SIGN_UP=false
|
||||||
|
- GF_INSTALL_PLUGINS=grafana-piechart-panel
|
||||||
|
# If you want to expose these ports outside your dev PC,
|
||||||
|
# remove the "127.0.0.1:" prefix
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:3000:3000
|
||||||
|
# On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and
|
||||||
|
# grafana/provisioning/datasources/datasource.yml
|
||||||
|
network_mode: 'host' # to test locally running service
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
|
||||||
|
name: sasiedzi
|
||||||
|
services:
|
||||||
|
postgresql:
|
||||||
|
image: postgres:17.0
|
||||||
|
# volumes:
|
||||||
|
# - ~/volumes/jhipster/sasiedzi/postgresql/:/var/lib/postgresql/data/
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=sasiedzi
|
||||||
|
- POSTGRES_PASSWORD=password
|
||||||
|
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER}']
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
# If you want to expose these ports outside your dev PC,
|
||||||
|
# remove the "127.0.0.1:" prefix
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:5432:5432
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Sample global config for monitoring JHipster applications
|
||||||
|
global:
|
||||||
|
scrape_interval: 15s # By default, scrape targets every 15 seconds.
|
||||||
|
evaluation_interval: 15s # By default, scrape targets every 15 seconds.
|
||||||
|
# scrape_timeout is set to the global default (10s).
|
||||||
|
|
||||||
|
# Attach these labels to any time series or alerts when communicating with
|
||||||
|
# external systems (federation, remote storage, Alertmanager).
|
||||||
|
external_labels:
|
||||||
|
monitor: 'jhipster'
|
||||||
|
|
||||||
|
# A scrape configuration containing exactly one endpoint to scrape:
|
||||||
|
# Here it's Prometheus itself.
|
||||||
|
scrape_configs:
|
||||||
|
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
|
||||||
|
- job_name: 'prometheus'
|
||||||
|
|
||||||
|
# Override the global default and scrape targets from this job every 5 seconds.
|
||||||
|
scrape_interval: 5s
|
||||||
|
|
||||||
|
# scheme defaults to 'http' enable https in case your application is server via https
|
||||||
|
#scheme: https
|
||||||
|
# basic auth is not needed by default. See https://www.jhipster.tech/monitoring/#configuring-metrics-forwarding for details
|
||||||
|
#basic_auth:
|
||||||
|
# username: admin
|
||||||
|
# password: admin
|
||||||
|
metrics_path: /management/prometheus
|
||||||
|
static_configs:
|
||||||
|
- targets:
|
||||||
|
# On MacOS, replace localhost by host.docker.internal
|
||||||
|
- localhost:8080
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
exec 3<>/dev/tcp/localhost/9990
|
||||||
|
|
||||||
|
echo -e "GET /health/ready HTTP/1.1\nhost: localhost:9990\n" >&3
|
||||||
|
|
||||||
|
timeout --preserve-status 1 cat <&3 | grep -m 1 status | grep -m 1 UP
|
||||||
|
ERROR=$?
|
||||||
|
|
||||||
|
exec 3<&-
|
||||||
|
exec 3>&-
|
||||||
|
|
||||||
|
exit $ERROR
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
|
||||||
|
name: sasiedzi
|
||||||
|
services:
|
||||||
|
postgresql:
|
||||||
|
extends:
|
||||||
|
file: ./postgresql.yml
|
||||||
|
service: postgresql
|
||||||
|
keycloak:
|
||||||
|
extends:
|
||||||
|
file: ./keycloak.yml
|
||||||
|
service: keycloak
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
|
||||||
|
name: sasiedzi
|
||||||
|
services:
|
||||||
|
sonar:
|
||||||
|
container_name: sonarqube
|
||||||
|
image: sonarqube:10.7.0-community
|
||||||
|
# Forced authentication redirect for UI is turned off for out of the box experience while trying out SonarQube
|
||||||
|
# For real use cases delete SONAR_FORCEAUTHENTICATION variable or set SONAR_FORCEAUTHENTICATION=true
|
||||||
|
environment:
|
||||||
|
- SONAR_FORCEAUTHENTICATION=false
|
||||||
|
# If you want to expose these ports outside your dev PC,
|
||||||
|
# remove the "127.0.0.1:" prefix
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:9001:9000
|
||||||
|
- 127.0.0.1:9000:9000
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.sasiedzi.event;
|
||||||
|
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
import tech.jhipster.config.DefaultProfileUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a helper Java class that provides an alternative to creating a {@code web.xml}.
|
||||||
|
* This will be invoked only when the application is deployed to a Servlet container like Tomcat, JBoss etc.
|
||||||
|
*/
|
||||||
|
public class ApplicationWebXml extends SpringBootServletInitializer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||||
|
// set a default to use when no profile is configured.
|
||||||
|
DefaultProfileUtil.addDefaultProfile(application.application());
|
||||||
|
return application.sources(SasiedziApp.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.sasiedzi.event;
|
||||||
|
|
||||||
|
import jakarta.annotation.Generated;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Generated(value = "JHipster", comments = "Generated by JHipster 8.7.2")
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@Target({ ElementType.TYPE })
|
||||||
|
public @interface GeneratedByJHipster {
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.sasiedzi.event;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.config.ApplicationProperties;
|
||||||
|
import com.sasiedzi.event.config.CRLFLogConverter;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import tech.jhipster.config.DefaultProfileUtil;
|
||||||
|
import tech.jhipster.config.JHipsterConstants;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableConfigurationProperties({ LiquibaseProperties.class, ApplicationProperties.class })
|
||||||
|
public class SasiedziApp {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(SasiedziApp.class);
|
||||||
|
|
||||||
|
private final Environment env;
|
||||||
|
|
||||||
|
public SasiedziApp(Environment env) {
|
||||||
|
this.env = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes sasiedzi.
|
||||||
|
* <p>
|
||||||
|
* Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile
|
||||||
|
* <p>
|
||||||
|
* You can find more information on how profiles work with JHipster on <a href="https://www.jhipster.tech/profiles/">https://www.jhipster.tech/profiles/</a>.
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
public void initApplication() {
|
||||||
|
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
|
||||||
|
if (
|
||||||
|
activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) &&
|
||||||
|
activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)
|
||||||
|
) {
|
||||||
|
LOG.error(
|
||||||
|
"You have misconfigured your application! It should not run " + "with both the 'dev' and 'prod' profiles at the same time."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) &&
|
||||||
|
activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)
|
||||||
|
) {
|
||||||
|
LOG.error(
|
||||||
|
"You have misconfigured your application! It should not " + "run with both the 'dev' and 'cloud' profiles at the same time."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main method, used to run the application.
|
||||||
|
*
|
||||||
|
* @param args the command line arguments.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication app = new SpringApplication(SasiedziApp.class);
|
||||||
|
DefaultProfileUtil.addDefaultProfile(app);
|
||||||
|
Environment env = app.run(args).getEnvironment();
|
||||||
|
logApplicationStartup(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logApplicationStartup(Environment env) {
|
||||||
|
String protocol = Optional.ofNullable(env.getProperty("server.ssl.key-store")).map(key -> "https").orElse("http");
|
||||||
|
String applicationName = env.getProperty("spring.application.name");
|
||||||
|
String serverPort = env.getProperty("server.port");
|
||||||
|
String contextPath = Optional.ofNullable(env.getProperty("server.servlet.context-path"))
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.orElse("/");
|
||||||
|
String hostAddress = "localhost";
|
||||||
|
try {
|
||||||
|
hostAddress = InetAddress.getLocalHost().getHostAddress();
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
LOG.warn("The host name could not be determined, using `localhost` as fallback");
|
||||||
|
}
|
||||||
|
LOG.info(
|
||||||
|
CRLFLogConverter.CRLF_SAFE_MARKER,
|
||||||
|
"""
|
||||||
|
|
||||||
|
----------------------------------------------------------
|
||||||
|
\tApplication '{}' is running! Access URLs:
|
||||||
|
\tLocal: \t\t{}://localhost:{}{}
|
||||||
|
\tExternal: \t{}://{}:{}{}
|
||||||
|
\tProfile(s): \t{}
|
||||||
|
----------------------------------------------------------""",
|
||||||
|
applicationName,
|
||||||
|
protocol,
|
||||||
|
serverPort,
|
||||||
|
contextPath,
|
||||||
|
protocol,
|
||||||
|
hostAddress,
|
||||||
|
serverPort,
|
||||||
|
contextPath,
|
||||||
|
env.getActiveProfiles().length == 0 ? env.getDefaultProfiles() : env.getActiveProfiles()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package com.sasiedzi.event.aop.logging;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.AfterThrowing;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.core.env.Profiles;
|
||||||
|
import tech.jhipster.config.JHipsterConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aspect for logging execution of service and repository Spring components.
|
||||||
|
*
|
||||||
|
* By default, it only runs with the "dev" profile.
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
public class LoggingAspect {
|
||||||
|
|
||||||
|
private final Environment env;
|
||||||
|
|
||||||
|
public LoggingAspect(Environment env) {
|
||||||
|
this.env = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointcut that matches all repositories, services and Web REST endpoints.
|
||||||
|
*/
|
||||||
|
@Pointcut(
|
||||||
|
"within(@org.springframework.stereotype.Repository *)" +
|
||||||
|
" || within(@org.springframework.stereotype.Service *)" +
|
||||||
|
" || within(@org.springframework.web.bind.annotation.RestController *)"
|
||||||
|
)
|
||||||
|
public void springBeanPointcut() {
|
||||||
|
// Method is empty as this is just a Pointcut, the implementations are in the advices.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointcut that matches all Spring beans in the application's main packages.
|
||||||
|
*/
|
||||||
|
@Pointcut(
|
||||||
|
"within(com.sasiedzi.event.repository..*)" +
|
||||||
|
" || within(com.sasiedzi.event.service..*)" +
|
||||||
|
" || within(com.sasiedzi.event.web.rest..*)"
|
||||||
|
)
|
||||||
|
public void applicationPackagePointcut() {
|
||||||
|
// Method is empty as this is just a Pointcut, the implementations are in the advices.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the {@link Logger} associated to the given {@link JoinPoint}.
|
||||||
|
*
|
||||||
|
* @param joinPoint join point we want the logger for.
|
||||||
|
* @return {@link Logger} associated to the given {@link JoinPoint}.
|
||||||
|
*/
|
||||||
|
private Logger logger(JoinPoint joinPoint) {
|
||||||
|
return LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringTypeName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advice that logs methods throwing exceptions.
|
||||||
|
*
|
||||||
|
* @param joinPoint join point for advice.
|
||||||
|
* @param e exception.
|
||||||
|
*/
|
||||||
|
@AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e")
|
||||||
|
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
|
||||||
|
if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) {
|
||||||
|
logger(joinPoint).error(
|
||||||
|
"Exception in {}() with cause = '{}' and exception = '{}'",
|
||||||
|
joinPoint.getSignature().getName(),
|
||||||
|
e.getCause() != null ? e.getCause() : "NULL",
|
||||||
|
e.getMessage(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
logger(joinPoint).error(
|
||||||
|
"Exception in {}() with cause = {}",
|
||||||
|
joinPoint.getSignature().getName(),
|
||||||
|
e.getCause() != null ? String.valueOf(e.getCause()) : "NULL"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advice that logs when a method is entered and exited.
|
||||||
|
*
|
||||||
|
* @param joinPoint join point for advice.
|
||||||
|
* @return result.
|
||||||
|
* @throws Throwable throws {@link IllegalArgumentException}.
|
||||||
|
*/
|
||||||
|
@Around("applicationPackagePointcut() && springBeanPointcut()")
|
||||||
|
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
|
Logger log = logger(joinPoint);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Object result = joinPoint.proceed();
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Exit: {}() with result = {}", joinPoint.getSignature().getName(), result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.error("Illegal argument: {} in {}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getName());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Logging aspect.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.aop.logging;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties specific to Sasiedzi.
|
||||||
|
* <p>
|
||||||
|
* Properties are configured in the {@code application.yml} file.
|
||||||
|
* See {@link tech.jhipster.config.JHipsterProperties} for a good example.
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
|
||||||
|
public class ApplicationProperties {
|
||||||
|
|
||||||
|
private final Liquibase liquibase = new Liquibase();
|
||||||
|
|
||||||
|
// jhipster-needle-application-properties-property
|
||||||
|
|
||||||
|
public Liquibase getLiquibase() {
|
||||||
|
return liquibase;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jhipster-needle-application-properties-property-getter
|
||||||
|
|
||||||
|
public static class Liquibase {
|
||||||
|
|
||||||
|
private Boolean asyncStart;
|
||||||
|
|
||||||
|
public Boolean getAsyncStart() {
|
||||||
|
return asyncStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAsyncStart(Boolean asyncStart) {
|
||||||
|
this.asyncStart = asyncStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// jhipster-needle-application-properties-property-class
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||||
|
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
|
||||||
|
import org.springframework.boot.autoconfigure.task.TaskExecutionProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import tech.jhipster.async.ExceptionHandlingAsyncTaskExecutor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAsync
|
||||||
|
@EnableScheduling
|
||||||
|
@Profile("!testdev & !testprod")
|
||||||
|
public class AsyncConfiguration implements AsyncConfigurer {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AsyncConfiguration.class);
|
||||||
|
|
||||||
|
private final TaskExecutionProperties taskExecutionProperties;
|
||||||
|
|
||||||
|
public AsyncConfiguration(TaskExecutionProperties taskExecutionProperties) {
|
||||||
|
this.taskExecutionProperties = taskExecutionProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Bean(name = "taskExecutor")
|
||||||
|
public Executor getAsyncExecutor() {
|
||||||
|
LOG.debug("Creating Async Task Executor");
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize());
|
||||||
|
executor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize());
|
||||||
|
executor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity());
|
||||||
|
executor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix());
|
||||||
|
return new ExceptionHandlingAsyncTaskExecutor(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||||
|
return new SimpleAsyncUncaughtExceptionHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.pattern.CompositeConverter;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.slf4j.Marker;
|
||||||
|
import org.slf4j.MarkerFactory;
|
||||||
|
import org.springframework.boot.ansi.AnsiColor;
|
||||||
|
import org.springframework.boot.ansi.AnsiElement;
|
||||||
|
import org.springframework.boot.ansi.AnsiOutput;
|
||||||
|
import org.springframework.boot.ansi.AnsiStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log filter to prevent attackers from forging log entries by submitting input containing CRLF characters.
|
||||||
|
* CRLF characters are replaced with a red colored _ character.
|
||||||
|
*
|
||||||
|
* @see <a href="https://owasp.org/www-community/attacks/Log_Injection">Log Forging Description</a>
|
||||||
|
* @see <a href="https://github.com/jhipster/generator-jhipster/issues/14949">JHipster issue</a>
|
||||||
|
*/
|
||||||
|
public class CRLFLogConverter extends CompositeConverter<ILoggingEvent> {
|
||||||
|
|
||||||
|
public static final Marker CRLF_SAFE_MARKER = MarkerFactory.getMarker("CRLF_SAFE");
|
||||||
|
|
||||||
|
private static final String[] SAFE_LOGS = {
|
||||||
|
"org.hibernate",
|
||||||
|
"org.springframework.boot.autoconfigure",
|
||||||
|
"org.springframework.boot.diagnostics",
|
||||||
|
};
|
||||||
|
private static final Map<String, AnsiElement> ELEMENTS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map<String, AnsiElement> ansiElements = new HashMap<>();
|
||||||
|
ansiElements.put("faint", AnsiStyle.FAINT);
|
||||||
|
ansiElements.put("red", AnsiColor.RED);
|
||||||
|
ansiElements.put("green", AnsiColor.GREEN);
|
||||||
|
ansiElements.put("yellow", AnsiColor.YELLOW);
|
||||||
|
ansiElements.put("blue", AnsiColor.BLUE);
|
||||||
|
ansiElements.put("magenta", AnsiColor.MAGENTA);
|
||||||
|
ansiElements.put("cyan", AnsiColor.CYAN);
|
||||||
|
ELEMENTS = Collections.unmodifiableMap(ansiElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String transform(ILoggingEvent event, String in) {
|
||||||
|
AnsiElement element = ELEMENTS.get(getFirstOption());
|
||||||
|
List<Marker> markers = event.getMarkerList();
|
||||||
|
if ((markers != null && !markers.isEmpty() && markers.get(0).contains(CRLF_SAFE_MARKER)) || isLoggerSafe(event)) {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
String replacement = element == null ? "_" : toAnsiString("_", element);
|
||||||
|
return in.replaceAll("[\n\r\t]", replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isLoggerSafe(ILoggingEvent event) {
|
||||||
|
for (String safeLogger : SAFE_LOGS) {
|
||||||
|
if (event.getLoggerName().startsWith(safeLogger)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String toAnsiString(String in, AnsiElement element) {
|
||||||
|
return AnsiOutput.toString(element, in);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application constants.
|
||||||
|
*/
|
||||||
|
public final class Constants {
|
||||||
|
|
||||||
|
// Regex for acceptable logins
|
||||||
|
public static final String LOGIN_REGEX = "^(?>[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)|(?>[_.@A-Za-z0-9-]+)$";
|
||||||
|
|
||||||
|
public static final String SYSTEM = "system";
|
||||||
|
public static final String DEFAULT_LANGUAGE = "en";
|
||||||
|
|
||||||
|
private Constants() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaRepositories({ "com.sasiedzi.event.repository" })
|
||||||
|
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
|
||||||
|
@EnableTransactionManagement
|
||||||
|
public class DatabaseConfiguration {}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.format.FormatterRegistry;
|
||||||
|
import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the converters to use the ISO format for dates by default.
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class DateTimeFormatConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addFormatters(FormatterRegistry registry) {
|
||||||
|
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
|
||||||
|
registrar.setUseIsoFormat(true);
|
||||||
|
registrar.registerFormatters(registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module;
|
||||||
|
import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module.Feature;
|
||||||
|
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class JacksonConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for Java date and time API.
|
||||||
|
* @return the corresponding Jackson module.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public JavaTimeModule javaTimeModule() {
|
||||||
|
return new JavaTimeModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Jdk8Module jdk8TimeModule() {
|
||||||
|
return new Jdk8Module();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Support for Hibernate types in Jackson.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public Hibernate6Module hibernate6Module() {
|
||||||
|
return new Hibernate6Module().configure(Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import liquibase.integration.spring.SpringLiquibase;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource;
|
||||||
|
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import tech.jhipster.config.JHipsterConstants;
|
||||||
|
import tech.jhipster.config.liquibase.SpringLiquibaseUtil;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class LiquibaseConfiguration {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(LiquibaseConfiguration.class);
|
||||||
|
|
||||||
|
private final Environment env;
|
||||||
|
|
||||||
|
public LiquibaseConfiguration(Environment env) {
|
||||||
|
this.env = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${application.liquibase.async-start:true}")
|
||||||
|
private Boolean asyncStart;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SpringLiquibase liquibase(
|
||||||
|
@Qualifier("taskExecutor") Executor executor,
|
||||||
|
LiquibaseProperties liquibaseProperties,
|
||||||
|
@LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource,
|
||||||
|
ObjectProvider<DataSource> dataSource,
|
||||||
|
DataSourceProperties dataSourceProperties
|
||||||
|
) {
|
||||||
|
SpringLiquibase liquibase;
|
||||||
|
if (Boolean.TRUE.equals(asyncStart)) {
|
||||||
|
liquibase = SpringLiquibaseUtil.createAsyncSpringLiquibase(
|
||||||
|
this.env,
|
||||||
|
executor,
|
||||||
|
liquibaseDataSource.getIfAvailable(),
|
||||||
|
liquibaseProperties,
|
||||||
|
dataSource.getIfUnique(),
|
||||||
|
dataSourceProperties
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
liquibase = SpringLiquibaseUtil.createSpringLiquibase(
|
||||||
|
liquibaseDataSource.getIfAvailable(),
|
||||||
|
liquibaseProperties,
|
||||||
|
dataSource.getIfUnique(),
|
||||||
|
dataSourceProperties
|
||||||
|
);
|
||||||
|
}
|
||||||
|
liquibase.setChangeLog("classpath:config/liquibase/master.xml");
|
||||||
|
liquibase.setContexts(liquibaseProperties.getContexts());
|
||||||
|
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
|
||||||
|
liquibase.setLiquibaseSchema(liquibaseProperties.getLiquibaseSchema());
|
||||||
|
liquibase.setLiquibaseTablespace(liquibaseProperties.getLiquibaseTablespace());
|
||||||
|
liquibase.setDatabaseChangeLogLockTable(liquibaseProperties.getDatabaseChangeLogLockTable());
|
||||||
|
liquibase.setDatabaseChangeLogTable(liquibaseProperties.getDatabaseChangeLogTable());
|
||||||
|
liquibase.setDropFirst(liquibaseProperties.isDropFirst());
|
||||||
|
liquibase.setLabelFilter(liquibaseProperties.getLabelFilter());
|
||||||
|
liquibase.setChangeLogParameters(liquibaseProperties.getParameters());
|
||||||
|
liquibase.setRollbackFile(liquibaseProperties.getRollbackFile());
|
||||||
|
liquibase.setTestRollbackOnUpdate(liquibaseProperties.isTestRollbackOnUpdate());
|
||||||
|
if (env.matchesProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) {
|
||||||
|
liquibase.setShouldRun(false);
|
||||||
|
} else {
|
||||||
|
liquibase.setShouldRun(liquibaseProperties.isEnabled());
|
||||||
|
LOG.debug("Configuring Liquibase");
|
||||||
|
}
|
||||||
|
return liquibase;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.aop.logging.LoggingAspect;
|
||||||
|
import org.springframework.context.annotation.*;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import tech.jhipster.config.JHipsterConstants;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAspectJAutoProxy
|
||||||
|
public class LoggingAspectConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
|
||||||
|
public LoggingAspect loggingAspect(Environment env) {
|
||||||
|
return new LoggingAspect(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import static tech.jhipster.config.logging.LoggingUtils.*;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import tech.jhipster.config.JHipsterProperties;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configures the console and Logstash log appenders from the app properties
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class LoggingConfiguration {
|
||||||
|
|
||||||
|
public LoggingConfiguration(
|
||||||
|
@Value("${spring.application.name}") String appName,
|
||||||
|
@Value("${server.port}") String serverPort,
|
||||||
|
JHipsterProperties jHipsterProperties,
|
||||||
|
ObjectMapper mapper
|
||||||
|
) throws JsonProcessingException {
|
||||||
|
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||||
|
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("app_name", appName);
|
||||||
|
map.put("app_port", serverPort);
|
||||||
|
String customFields = mapper.writeValueAsString(map);
|
||||||
|
|
||||||
|
JHipsterProperties.Logging loggingProperties = jHipsterProperties.getLogging();
|
||||||
|
JHipsterProperties.Logging.Logstash logstashProperties = loggingProperties.getLogstash();
|
||||||
|
|
||||||
|
if (loggingProperties.isUseJsonFormat()) {
|
||||||
|
addJsonConsoleAppender(context, customFields);
|
||||||
|
}
|
||||||
|
if (logstashProperties.isEnabled()) {
|
||||||
|
addLogstashTcpSocketAppender(context, customFields, logstashProperties);
|
||||||
|
}
|
||||||
|
if (loggingProperties.isUseJsonFormat() || logstashProperties.isEnabled()) {
|
||||||
|
addContextListener(context, customFields, loggingProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
|
||||||
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class OAuth2Configuration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
||||||
|
ClientRegistrationRepository clientRegistrationRepository,
|
||||||
|
OAuth2AuthorizedClientRepository authorizedClientRepository
|
||||||
|
) {
|
||||||
|
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
|
||||||
|
clientRegistrationRepository,
|
||||||
|
authorizedClientRepository
|
||||||
|
);
|
||||||
|
|
||||||
|
authorizedClientManager.setAuthorizedClientProvider(
|
||||||
|
OAuth2AuthorizedClientProviderBuilder.builder()
|
||||||
|
.authorizationCode()
|
||||||
|
.refreshToken(builder -> builder.clockSkew(Duration.ofMinutes(1)))
|
||||||
|
.clientCredentials()
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
return authorizedClientManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
|
import static org.springframework.security.oauth2.core.oidc.StandardClaimNames.PREFERRED_USERNAME;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.security.*;
|
||||||
|
import com.sasiedzi.event.security.SecurityUtils;
|
||||||
|
import com.sasiedzi.event.security.oauth2.AudienceValidator;
|
||||||
|
import com.sasiedzi.event.security.oauth2.CustomClaimConverter;
|
||||||
|
import com.sasiedzi.event.web.filter.SpaWebFilter;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
|
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
||||||
|
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||||
|
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.*;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.csrf.*;
|
||||||
|
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
import tech.jhipster.config.JHipsterProperties;
|
||||||
|
import tech.jhipster.web.filter.CookieCsrfFilter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableMethodSecurity(securedEnabled = true)
|
||||||
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
|
private final JHipsterProperties jHipsterProperties;
|
||||||
|
|
||||||
|
@Value("${spring.security.oauth2.client.provider.oidc.issuer-uri}")
|
||||||
|
private String issuerUri;
|
||||||
|
|
||||||
|
public SecurityConfiguration(JHipsterProperties jHipsterProperties) {
|
||||||
|
this.jHipsterProperties = jHipsterProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
|
||||||
|
http
|
||||||
|
.cors(withDefaults())
|
||||||
|
.csrf(csrf ->
|
||||||
|
csrf
|
||||||
|
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
||||||
|
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler())
|
||||||
|
)
|
||||||
|
.addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class)
|
||||||
|
.addFilterAfter(new CookieCsrfFilter(), BasicAuthenticationFilter.class)
|
||||||
|
.headers(headers ->
|
||||||
|
headers
|
||||||
|
.contentSecurityPolicy(csp -> csp.policyDirectives(jHipsterProperties.getSecurity().getContentSecurityPolicy()))
|
||||||
|
.frameOptions(FrameOptionsConfig::sameOrigin)
|
||||||
|
.referrerPolicy(referrer -> referrer.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN))
|
||||||
|
.permissionsPolicy(permissions ->
|
||||||
|
permissions.policy(
|
||||||
|
"camera=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.authorizeHttpRequests(authz ->
|
||||||
|
// prettier-ignore
|
||||||
|
authz
|
||||||
|
.requestMatchers(mvc.pattern("/index.html"), mvc.pattern("/*.js"), mvc.pattern("/*.txt"), mvc.pattern("/*.json"), mvc.pattern("/*.map"), mvc.pattern("/*.css")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/*.ico"), mvc.pattern("/*.png"), mvc.pattern("/*.svg"), mvc.pattern("/*.webapp")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/assets/**")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/swagger-ui/**")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/api/authenticate")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/api/auth-info")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/api/admin/**")).hasAuthority(AuthoritiesConstants.ADMIN)
|
||||||
|
.requestMatchers(mvc.pattern("/api/**")).authenticated()
|
||||||
|
.requestMatchers(mvc.pattern("/v3/api-docs/**")).hasAuthority(AuthoritiesConstants.ADMIN)
|
||||||
|
.requestMatchers(mvc.pattern("/management/health")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/management/health/**")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/management/info")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/management/prometheus")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/management/**")).hasAuthority(AuthoritiesConstants.ADMIN)
|
||||||
|
)
|
||||||
|
.oauth2Login(oauth2 -> oauth2.loginPage("/").userInfoEndpoint(userInfo -> userInfo.oidcUserService(this.oidcUserService())))
|
||||||
|
.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(authenticationConverter())))
|
||||||
|
.oauth2Client(withDefaults());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
|
||||||
|
return new MvcRequestMatcher.Builder(introspector);
|
||||||
|
}
|
||||||
|
|
||||||
|
Converter<Jwt, AbstractAuthenticationToken> authenticationConverter() {
|
||||||
|
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
|
||||||
|
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(
|
||||||
|
new Converter<Jwt, Collection<GrantedAuthority>>() {
|
||||||
|
@Override
|
||||||
|
public Collection<GrantedAuthority> convert(Jwt jwt) {
|
||||||
|
return SecurityUtils.extractAuthorityFromClaims(jwt.getClaims());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
jwtAuthenticationConverter.setPrincipalClaimName(PREFERRED_USERNAME);
|
||||||
|
return jwtAuthenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
|
||||||
|
final OidcUserService delegate = new OidcUserService();
|
||||||
|
|
||||||
|
return userRequest -> {
|
||||||
|
OidcUser oidcUser = delegate.loadUser(userRequest);
|
||||||
|
return new DefaultOidcUser(oidcUser.getAuthorities(), oidcUser.getIdToken(), oidcUser.getUserInfo(), PREFERRED_USERNAME);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map authorities from "groups" or "roles" claim in ID Token.
|
||||||
|
*
|
||||||
|
* @return a {@link GrantedAuthoritiesMapper} that maps groups from
|
||||||
|
* the IdP to Spring Security Authorities.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
||||||
|
return authorities -> {
|
||||||
|
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
|
||||||
|
|
||||||
|
authorities.forEach(authority -> {
|
||||||
|
// Check for OidcUserAuthority because Spring Security 5.2 returns
|
||||||
|
// each scope as a GrantedAuthority, which we don't care about.
|
||||||
|
if (authority instanceof OidcUserAuthority) {
|
||||||
|
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
|
||||||
|
mappedAuthorities.addAll(SecurityUtils.extractAuthorityFromClaims(oidcUserAuthority.getUserInfo().getClaims()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return mappedAuthorities;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
JwtDecoder jwtDecoder(ClientRegistrationRepository clientRegistrationRepository, RestTemplateBuilder restTemplateBuilder) {
|
||||||
|
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuerUri);
|
||||||
|
|
||||||
|
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(jHipsterProperties.getSecurity().getOauth2().getAudience());
|
||||||
|
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
|
||||||
|
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
|
||||||
|
|
||||||
|
jwtDecoder.setJwtValidator(withAudience);
|
||||||
|
jwtDecoder.setClaimSetConverter(
|
||||||
|
new CustomClaimConverter(clientRegistrationRepository.findByRegistrationId("oidc"), restTemplateBuilder.build())
|
||||||
|
);
|
||||||
|
|
||||||
|
return jwtDecoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom CSRF handler to provide BREACH protection.
|
||||||
|
*
|
||||||
|
* @see <a href="https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html#csrf-integration-javascript-spa">Spring Security Documentation - Integrating with CSRF Protection</a>
|
||||||
|
* @see <a href="https://github.com/jhipster/generator-jhipster/pull/25907">JHipster - use customized SpaCsrfTokenRequestHandler to handle CSRF token</a>
|
||||||
|
* @see <a href="https://stackoverflow.com/q/74447118/65681">CSRF protection not working with Spring Security 6</a>
|
||||||
|
*/
|
||||||
|
static final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
|
||||||
|
|
||||||
|
private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
|
||||||
|
/*
|
||||||
|
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
|
||||||
|
* the CsrfToken when it is rendered in the response body.
|
||||||
|
*/
|
||||||
|
this.delegate.handle(request, response, csrfToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
|
||||||
|
/*
|
||||||
|
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
|
||||||
|
* to resolve the CsrfToken. This applies when a single-page application includes
|
||||||
|
* the header value automatically, which was obtained via a cookie containing the
|
||||||
|
* raw CsrfToken.
|
||||||
|
*/
|
||||||
|
if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
|
||||||
|
return super.resolveCsrfTokenValue(request, csrfToken);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* In all other cases (e.g. if the request contains a request parameter), use
|
||||||
|
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
|
||||||
|
* when a server-side rendered form includes the _csrf request parameter as a
|
||||||
|
* hidden input.
|
||||||
|
*/
|
||||||
|
return this.delegate.resolveCsrfTokenValue(request, csrfToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.http.CacheControl;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
import tech.jhipster.config.JHipsterConstants;
|
||||||
|
import tech.jhipster.config.JHipsterProperties;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Profile({ JHipsterConstants.SPRING_PROFILE_PRODUCTION })
|
||||||
|
public class StaticResourcesWebConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
protected static final String[] RESOURCE_LOCATIONS = { "classpath:/static/", "classpath:/static/content/", "classpath:/static/i18n/" };
|
||||||
|
protected static final String[] RESOURCE_PATHS = { "/*.js", "/*.css", "/*.svg", "/*.png", "*.ico", "/content/**", "/i18n/*" };
|
||||||
|
|
||||||
|
private final JHipsterProperties jhipsterProperties;
|
||||||
|
|
||||||
|
public StaticResourcesWebConfiguration(JHipsterProperties jHipsterProperties) {
|
||||||
|
this.jhipsterProperties = jHipsterProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
ResourceHandlerRegistration resourceHandlerRegistration = appendResourceHandler(registry);
|
||||||
|
initializeResourceHandler(resourceHandlerRegistration);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ResourceHandlerRegistration appendResourceHandler(ResourceHandlerRegistry registry) {
|
||||||
|
return registry.addResourceHandler(RESOURCE_PATHS);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeResourceHandler(ResourceHandlerRegistration resourceHandlerRegistration) {
|
||||||
|
resourceHandlerRegistration.addResourceLocations(RESOURCE_LOCATIONS).setCacheControl(getCacheControl());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CacheControl getCacheControl() {
|
||||||
|
return CacheControl.maxAge(getJHipsterHttpCacheProperty(), TimeUnit.DAYS).cachePublic();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getJHipsterHttpCacheProperty() {
|
||||||
|
return jhipsterProperties.getHttp().getCache().getTimeToLiveInDays();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.sasiedzi.event.config;
|
||||||
|
|
||||||
|
import static java.net.URLDecoder.decode;
|
||||||
|
|
||||||
|
import jakarta.servlet.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.web.server.*;
|
||||||
|
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||||
|
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
import tech.jhipster.config.JHipsterProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration of web application with Servlet 3.0 APIs.
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer<WebServerFactory> {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(WebConfigurer.class);
|
||||||
|
|
||||||
|
private final Environment env;
|
||||||
|
|
||||||
|
private final JHipsterProperties jHipsterProperties;
|
||||||
|
|
||||||
|
public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) {
|
||||||
|
this.env = env;
|
||||||
|
this.jHipsterProperties = jHipsterProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartup(ServletContext servletContext) {
|
||||||
|
if (env.getActiveProfiles().length != 0) {
|
||||||
|
LOG.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("Web application fully configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize the Servlet engine: Mime types, the document root, the cache.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void customize(WebServerFactory server) {
|
||||||
|
// When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets.
|
||||||
|
setLocationForStaticAssets(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLocationForStaticAssets(WebServerFactory server) {
|
||||||
|
if (server instanceof ConfigurableServletWebServerFactory servletWebServer) {
|
||||||
|
File root;
|
||||||
|
String prefixPath = resolvePathPrefix();
|
||||||
|
root = new File(prefixPath + "target/classes/static/");
|
||||||
|
if (root.exists() && root.isDirectory()) {
|
||||||
|
servletWebServer.setDocumentRoot(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve path prefix to static resources.
|
||||||
|
*/
|
||||||
|
private String resolvePathPrefix() {
|
||||||
|
String fullExecutablePath = decode(this.getClass().getResource("").getPath(), StandardCharsets.UTF_8);
|
||||||
|
String rootPath = Paths.get(".").toUri().normalize().getPath();
|
||||||
|
String extractedPath = fullExecutablePath.replace(rootPath, "");
|
||||||
|
int extractionEndIndex = extractedPath.indexOf("target/");
|
||||||
|
if (extractionEndIndex <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return extractedPath.substring(0, extractionEndIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsFilter corsFilter() {
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
CorsConfiguration config = jHipsterProperties.getCors();
|
||||||
|
if (!CollectionUtils.isEmpty(config.getAllowedOrigins()) || !CollectionUtils.isEmpty(config.getAllowedOriginPatterns())) {
|
||||||
|
LOG.debug("Registering CORS filter");
|
||||||
|
source.registerCorsConfiguration("/api/**", config);
|
||||||
|
source.registerCorsConfiguration("/management/**", config);
|
||||||
|
source.registerCorsConfiguration("/v3/api-docs", config);
|
||||||
|
source.registerCorsConfiguration("/swagger-ui/**", config);
|
||||||
|
}
|
||||||
|
return new CorsFilter(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Application configuration.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.config;
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.sasiedzi.event.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.EntityListeners;
|
||||||
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
import org.springframework.data.annotation.CreatedBy;
|
||||||
|
import org.springframework.data.annotation.CreatedDate;
|
||||||
|
import org.springframework.data.annotation.LastModifiedBy;
|
||||||
|
import org.springframework.data.annotation.LastModifiedDate;
|
||||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base abstract class for entities which will hold definitions for created, last modified, created by,
|
||||||
|
* last modified by attributes.
|
||||||
|
*/
|
||||||
|
@MappedSuperclass
|
||||||
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
|
@JsonIgnoreProperties(value = { "createdBy", "createdDate", "lastModifiedBy", "lastModifiedDate" }, allowGetters = true)
|
||||||
|
public abstract class AbstractAuditingEntity<T> implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public abstract T getId();
|
||||||
|
|
||||||
|
@CreatedBy
|
||||||
|
@Column(name = "created_by", nullable = false, length = 50, updatable = false)
|
||||||
|
private String createdBy;
|
||||||
|
|
||||||
|
@CreatedDate
|
||||||
|
@Column(name = "created_date", updatable = false)
|
||||||
|
private Instant createdDate = Instant.now();
|
||||||
|
|
||||||
|
@LastModifiedBy
|
||||||
|
@Column(name = "last_modified_by", length = 50)
|
||||||
|
private String lastModifiedBy;
|
||||||
|
|
||||||
|
@LastModifiedDate
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Instant lastModifiedDate = Instant.now();
|
||||||
|
|
||||||
|
public String getCreatedBy() {
|
||||||
|
return createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedBy(String createdBy) {
|
||||||
|
this.createdBy = createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getCreatedDate() {
|
||||||
|
return createdDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedDate(Instant createdDate) {
|
||||||
|
this.createdDate = createdDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastModifiedBy() {
|
||||||
|
return lastModifiedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastModifiedBy(String lastModifiedBy) {
|
||||||
|
this.lastModifiedBy = lastModifiedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getLastModifiedDate() {
|
||||||
|
return lastModifiedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastModifiedDate(Instant lastModifiedDate) {
|
||||||
|
this.lastModifiedDate = lastModifiedDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.sasiedzi.event.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.springframework.data.domain.Persistable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Authority.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "jhi_authority")
|
||||||
|
@JsonIgnoreProperties(value = { "new", "id" })
|
||||||
|
@SuppressWarnings("common-java:DuplicatedBlocks")
|
||||||
|
public class Authority implements Serializable, Persistable<String> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Size(max = 50)
|
||||||
|
@Id
|
||||||
|
@Column(name = "name", length = 50, nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@org.springframework.data.annotation.Transient
|
||||||
|
@Transient
|
||||||
|
private boolean isPersisted;
|
||||||
|
|
||||||
|
// jhipster-needle-entity-add-field - JHipster will add fields here
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Authority name(String name) {
|
||||||
|
this.setName(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostLoad
|
||||||
|
@PostPersist
|
||||||
|
public void updateEntityState() {
|
||||||
|
this.setIsPersisted();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.springframework.data.annotation.Transient
|
||||||
|
@Transient
|
||||||
|
@Override
|
||||||
|
public boolean isNew() {
|
||||||
|
return !this.isPersisted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Authority setIsPersisted() {
|
||||||
|
this.isPersisted = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof Authority)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return getName() != null && getName().equals(((Authority) o).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Authority{" +
|
||||||
|
"name=" + getName() +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
package com.sasiedzi.event.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.sasiedzi.event.config.Constants;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.Email;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.hibernate.annotations.BatchSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A user.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "jhi_user")
|
||||||
|
public class User extends AbstractAuditingEntity<String> implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||||
|
@Size(min = 1, max = 50)
|
||||||
|
@Column(length = 50, unique = true, nullable = false)
|
||||||
|
private String login;
|
||||||
|
|
||||||
|
@Size(max = 50)
|
||||||
|
@Column(name = "first_name", length = 50)
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
@Size(max = 50)
|
||||||
|
@Column(name = "last_name", length = 50)
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
@Email
|
||||||
|
@Size(min = 5, max = 254)
|
||||||
|
@Column(length = 254, unique = true)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean activated = false;
|
||||||
|
|
||||||
|
@Size(min = 2, max = 10)
|
||||||
|
@Column(name = "lang_key", length = 10)
|
||||||
|
private String langKey;
|
||||||
|
|
||||||
|
@Size(max = 256)
|
||||||
|
@Column(name = "image_url", length = 256)
|
||||||
|
private String imageUrl;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = "jhi_user_authority",
|
||||||
|
joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") },
|
||||||
|
inverseJoinColumns = { @JoinColumn(name = "authority_name", referencedColumnName = "name") }
|
||||||
|
)
|
||||||
|
@BatchSize(size = 20)
|
||||||
|
private Set<Authority> authorities = new HashSet<>();
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lowercase the login before saving it in database
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = StringUtils.lowerCase(login, Locale.ENGLISH);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImageUrl() {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageUrl(String imageUrl) {
|
||||||
|
this.imageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActivated() {
|
||||||
|
return activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivated(boolean activated) {
|
||||||
|
this.activated = activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLangKey() {
|
||||||
|
return langKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLangKey(String langKey) {
|
||||||
|
this.langKey = langKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Authority> getAuthorities() {
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthorities(Set<Authority> authorities) {
|
||||||
|
this.authorities = authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof User)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return id != null && id.equals(((User) o).id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// see https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
|
||||||
|
return getClass().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "User{" +
|
||||||
|
"login='" + login + '\'' +
|
||||||
|
", firstName='" + firstName + '\'' +
|
||||||
|
", lastName='" + lastName + '\'' +
|
||||||
|
", email='" + email + '\'' +
|
||||||
|
", imageUrl='" + imageUrl + '\'' +
|
||||||
|
", activated='" + activated + '\'' +
|
||||||
|
", langKey='" + langKey + '\'' +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Domain objects.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.domain;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Application root.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.sasiedzi.event.repository;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.domain.Authority;
|
||||||
|
import org.springframework.data.jpa.repository.*;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Data JPA repository for the Authority entity.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Repository
|
||||||
|
public interface AuthorityRepository extends JpaRepository<Authority, String> {}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.sasiedzi.event.repository;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.domain.User;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.domain.*;
|
||||||
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Data JPA repository for the {@link User} entity.
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface UserRepository extends JpaRepository<User, String> {
|
||||||
|
Optional<User> findOneByLogin(String login);
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = "authorities")
|
||||||
|
Optional<User> findOneWithAuthoritiesByLogin(String login);
|
||||||
|
|
||||||
|
Page<User> findAllByIdNotNullAndActivatedIsTrue(Pageable pageable);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Repository layer.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.repository;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.sasiedzi.event.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants for Spring Security authorities.
|
||||||
|
*/
|
||||||
|
public final class AuthoritiesConstants {
|
||||||
|
|
||||||
|
public static final String ADMIN = "ROLE_ADMIN";
|
||||||
|
|
||||||
|
public static final String USER = "ROLE_USER";
|
||||||
|
|
||||||
|
public static final String ANONYMOUS = "ROLE_ANONYMOUS";
|
||||||
|
|
||||||
|
private AuthoritiesConstants() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package com.sasiedzi.event.security;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for Spring Security.
|
||||||
|
*/
|
||||||
|
public final class SecurityUtils {
|
||||||
|
|
||||||
|
public static final String CLAIMS_NAMESPACE = "https://www.jhipster.tech/";
|
||||||
|
|
||||||
|
private SecurityUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the login of the current user.
|
||||||
|
*
|
||||||
|
* @return the login of the current user.
|
||||||
|
*/
|
||||||
|
public static Optional<String> getCurrentUserLogin() {
|
||||||
|
SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||||
|
return Optional.ofNullable(extractPrincipal(securityContext.getAuthentication()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractPrincipal(Authentication authentication) {
|
||||||
|
if (authentication == null) {
|
||||||
|
return null;
|
||||||
|
} else if (authentication.getPrincipal() instanceof UserDetails springSecurityUser) {
|
||||||
|
return springSecurityUser.getUsername();
|
||||||
|
} else if (authentication instanceof JwtAuthenticationToken) {
|
||||||
|
return (String) ((JwtAuthenticationToken) authentication).getToken().getClaims().get("preferred_username");
|
||||||
|
} else if (authentication.getPrincipal() instanceof DefaultOidcUser) {
|
||||||
|
Map<String, Object> attributes = ((DefaultOidcUser) authentication.getPrincipal()).getAttributes();
|
||||||
|
if (attributes.containsKey("preferred_username")) {
|
||||||
|
return (String) attributes.get("preferred_username");
|
||||||
|
}
|
||||||
|
} else if (authentication.getPrincipal() instanceof String s) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a user is authenticated.
|
||||||
|
*
|
||||||
|
* @return true if the user is authenticated, false otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean isAuthenticated() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
return authentication != null && getAuthorities(authentication).noneMatch(AuthoritiesConstants.ANONYMOUS::equals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current user has any of the authorities.
|
||||||
|
*
|
||||||
|
* @param authorities the authorities to check.
|
||||||
|
* @return true if the current user has any of the authorities, false otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean hasCurrentUserAnyOfAuthorities(String... authorities) {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
return (
|
||||||
|
authentication != null && getAuthorities(authentication).anyMatch(authority -> Arrays.asList(authorities).contains(authority))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current user has none of the authorities.
|
||||||
|
*
|
||||||
|
* @param authorities the authorities to check.
|
||||||
|
* @return true if the current user has none of the authorities, false otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean hasCurrentUserNoneOfAuthorities(String... authorities) {
|
||||||
|
return !hasCurrentUserAnyOfAuthorities(authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current user has a specific authority.
|
||||||
|
*
|
||||||
|
* @param authority the authority to check.
|
||||||
|
* @return true if the current user has the authority, false otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean hasCurrentUserThisAuthority(String authority) {
|
||||||
|
return hasCurrentUserAnyOfAuthorities(authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<String> getAuthorities(Authentication authentication) {
|
||||||
|
Collection<? extends GrantedAuthority> authorities = authentication instanceof JwtAuthenticationToken
|
||||||
|
? extractAuthorityFromClaims(((JwtAuthenticationToken) authentication).getToken().getClaims())
|
||||||
|
: authentication.getAuthorities();
|
||||||
|
return authorities.stream().map(GrantedAuthority::getAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<GrantedAuthority> extractAuthorityFromClaims(Map<String, Object> claims) {
|
||||||
|
return mapRolesToGrantedAuthorities(getRolesFromClaims(claims));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Collection<String> getRolesFromClaims(Map<String, Object> claims) {
|
||||||
|
return (Collection<String>) claims.getOrDefault(
|
||||||
|
"groups",
|
||||||
|
claims.getOrDefault("roles", claims.getOrDefault(CLAIMS_NAMESPACE + "roles", new ArrayList<>()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<GrantedAuthority> mapRolesToGrantedAuthorities(Collection<String> roles) {
|
||||||
|
return roles.stream().filter(role -> role.startsWith("ROLE_")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.sasiedzi.event.security;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.config.Constants;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.domain.AuditorAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link AuditorAware} based on Spring Security.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SpringSecurityAuditorAware implements AuditorAware<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<String> getCurrentAuditor() {
|
||||||
|
return Optional.of(SecurityUtils.getCurrentUserLogin().orElse(Constants.SYSTEM));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.sasiedzi.event.security.oauth2;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AudienceValidator.class);
|
||||||
|
private final OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);
|
||||||
|
|
||||||
|
private final List<String> allowedAudience;
|
||||||
|
|
||||||
|
public AudienceValidator(List<String> allowedAudience) {
|
||||||
|
Assert.notEmpty(allowedAudience, "Allowed audience should not be null or empty.");
|
||||||
|
this.allowedAudience = allowedAudience;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OAuth2TokenValidatorResult validate(Jwt jwt) {
|
||||||
|
List<String> audience = jwt.getAudience();
|
||||||
|
if (audience.stream().anyMatch(allowedAudience::contains)) {
|
||||||
|
return OAuth2TokenValidatorResult.success();
|
||||||
|
} else {
|
||||||
|
LOG.warn("Invalid audience: {}", audience);
|
||||||
|
return OAuth2TokenValidatorResult.failure(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package com.sasiedzi.event.security.oauth2;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.sasiedzi.event.security.SecurityUtils;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.jwt.MappedJwtClaimSetConverter;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claim converter to add custom claims by retrieving the user from the userinfo endpoint.
|
||||||
|
*/
|
||||||
|
public class CustomClaimConverter implements Converter<Map<String, Object>, Map<String, Object>> {
|
||||||
|
|
||||||
|
private final BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
|
||||||
|
|
||||||
|
private final MappedJwtClaimSetConverter delegate = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate;
|
||||||
|
|
||||||
|
private final ClientRegistration registration;
|
||||||
|
|
||||||
|
// See https://github.com/jhipster/generator-jhipster/issues/18868
|
||||||
|
// We don't use a distributed cache or the user selected cache implementation here on purpose
|
||||||
|
private final Cache<String, ObjectNode> users = Caffeine.newBuilder()
|
||||||
|
.maximumSize(10_000)
|
||||||
|
.expireAfterWrite(Duration.ofHours(1))
|
||||||
|
.recordStats()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public CustomClaimConverter(ClientRegistration registration, RestTemplate restTemplate) {
|
||||||
|
this.registration = registration;
|
||||||
|
this.restTemplate = restTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> convert(Map<String, Object> claims) {
|
||||||
|
Map<String, Object> convertedClaims = this.delegate.convert(claims);
|
||||||
|
// Only look up user information if identity claims are missing
|
||||||
|
if (claims.containsKey("given_name") && claims.containsKey("family_name")) {
|
||||||
|
return convertedClaims;
|
||||||
|
}
|
||||||
|
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||||
|
if (attributes instanceof ServletRequestAttributes) {
|
||||||
|
// Retrieve and set the token
|
||||||
|
String token = bearerTokenResolver.resolve(((ServletRequestAttributes) attributes).getRequest());
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setBearerAuth(token);
|
||||||
|
|
||||||
|
// Retrieve user info from OAuth provider if not already loaded
|
||||||
|
ObjectNode user = users.get(claims.get("sub").toString(), s -> {
|
||||||
|
ResponseEntity<ObjectNode> userInfo = restTemplate.exchange(
|
||||||
|
registration.getProviderDetails().getUserInfoEndpoint().getUri(),
|
||||||
|
HttpMethod.GET,
|
||||||
|
new HttpEntity<String>(headers),
|
||||||
|
ObjectNode.class
|
||||||
|
);
|
||||||
|
return userInfo.getBody();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add custom claims
|
||||||
|
if (user != null) {
|
||||||
|
convertedClaims.put("preferred_username", user.get("preferred_username").asText());
|
||||||
|
if (user.has("given_name")) {
|
||||||
|
convertedClaims.put("given_name", user.get("given_name").asText());
|
||||||
|
}
|
||||||
|
if (user.has("family_name")) {
|
||||||
|
convertedClaims.put("family_name", user.get("family_name").asText());
|
||||||
|
}
|
||||||
|
if (user.has("email")) {
|
||||||
|
convertedClaims.put("email", user.get("email").asText());
|
||||||
|
}
|
||||||
|
// Allow full name in a name claim - happens with Auth0
|
||||||
|
if (user.has("name")) {
|
||||||
|
String[] name = user.get("name").asText().split("\\s+");
|
||||||
|
if (name.length > 0) {
|
||||||
|
convertedClaims.put("given_name", name[0]);
|
||||||
|
convertedClaims.put("family_name", String.join(" ", Arrays.copyOfRange(name, 1, name.length)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (user.has("groups")) {
|
||||||
|
List<String> groups = StreamSupport.stream(user.get("groups").spliterator(), false).map(JsonNode::asText).toList();
|
||||||
|
convertedClaims.put("groups", groups);
|
||||||
|
}
|
||||||
|
if (user.has(SecurityUtils.CLAIMS_NAMESPACE + "roles")) {
|
||||||
|
List<String> roles = StreamSupport.stream(user.get(SecurityUtils.CLAIMS_NAMESPACE + "roles").spliterator(), false)
|
||||||
|
.map(JsonNode::asText)
|
||||||
|
.toList();
|
||||||
|
convertedClaims.put("roles", roles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return convertedClaims;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* This package file was generated by JHipster
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.security.oauth2;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Application security utilities.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.security;
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
package com.sasiedzi.event.service;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.config.Constants;
|
||||||
|
import com.sasiedzi.event.domain.Authority;
|
||||||
|
import com.sasiedzi.event.domain.User;
|
||||||
|
import com.sasiedzi.event.repository.AuthorityRepository;
|
||||||
|
import com.sasiedzi.event.repository.UserRepository;
|
||||||
|
import com.sasiedzi.event.security.SecurityUtils;
|
||||||
|
import com.sasiedzi.event.service.dto.AdminUserDTO;
|
||||||
|
import com.sasiedzi.event.service.dto.UserDTO;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service class for managing users.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(UserService.class);
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
private final AuthorityRepository authorityRepository;
|
||||||
|
|
||||||
|
public UserService(UserRepository userRepository, AuthorityRepository authorityRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.authorityRepository = authorityRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update basic information (first name, last name, email, language) for the current user.
|
||||||
|
*
|
||||||
|
* @param firstName first name of user.
|
||||||
|
* @param lastName last name of user.
|
||||||
|
* @param email email id of user.
|
||||||
|
* @param langKey language key.
|
||||||
|
* @param imageUrl image URL of user.
|
||||||
|
*/
|
||||||
|
public void updateUser(String firstName, String lastName, String email, String langKey, String imageUrl) {
|
||||||
|
SecurityUtils.getCurrentUserLogin()
|
||||||
|
.flatMap(userRepository::findOneByLogin)
|
||||||
|
.ifPresent(user -> {
|
||||||
|
user.setFirstName(firstName);
|
||||||
|
user.setLastName(lastName);
|
||||||
|
if (email != null) {
|
||||||
|
user.setEmail(email.toLowerCase());
|
||||||
|
}
|
||||||
|
user.setLangKey(langKey);
|
||||||
|
user.setImageUrl(imageUrl);
|
||||||
|
userRepository.save(user);
|
||||||
|
LOG.debug("Changed Information for User: {}", user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Page<AdminUserDTO> getAllManagedUsers(Pageable pageable) {
|
||||||
|
return userRepository.findAll(pageable).map(AdminUserDTO::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Page<UserDTO> getAllPublicUsers(Pageable pageable) {
|
||||||
|
return userRepository.findAllByIdNotNullAndActivatedIsTrue(pageable).map(UserDTO::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<User> getUserWithAuthoritiesByLogin(String login) {
|
||||||
|
return userRepository.findOneWithAuthoritiesByLogin(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of all the authorities.
|
||||||
|
* @return a list of all the authorities.
|
||||||
|
*/
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<String> getAuthorities() {
|
||||||
|
return authorityRepository.findAll().stream().map(Authority::getName).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private User syncUserWithIdP(Map<String, Object> details, User user) {
|
||||||
|
// save authorities in to sync user roles/groups between IdP and JHipster's local database
|
||||||
|
Collection<String> dbAuthorities = getAuthorities();
|
||||||
|
Collection<String> userAuthorities = user.getAuthorities().stream().map(Authority::getName).toList();
|
||||||
|
for (String authority : userAuthorities) {
|
||||||
|
if (!dbAuthorities.contains(authority)) {
|
||||||
|
LOG.debug("Saving authority '{}' in local database", authority);
|
||||||
|
Authority authorityToSave = new Authority();
|
||||||
|
authorityToSave.setName(authority);
|
||||||
|
authorityRepository.save(authorityToSave);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// save account in to sync users between IdP and JHipster's local database
|
||||||
|
Optional<User> existingUser = userRepository.findOneByLogin(user.getLogin());
|
||||||
|
if (existingUser.isPresent()) {
|
||||||
|
// if IdP sends last updated information, use it to determine if an update should happen
|
||||||
|
if (details.get("updated_at") != null) {
|
||||||
|
Instant dbModifiedDate = existingUser.orElseThrow().getLastModifiedDate();
|
||||||
|
Instant idpModifiedDate;
|
||||||
|
if (details.get("updated_at") instanceof Instant) {
|
||||||
|
idpModifiedDate = (Instant) details.get("updated_at");
|
||||||
|
} else {
|
||||||
|
idpModifiedDate = Instant.ofEpochSecond((Integer) details.get("updated_at"));
|
||||||
|
}
|
||||||
|
if (idpModifiedDate.isAfter(dbModifiedDate)) {
|
||||||
|
LOG.debug("Updating user '{}' in local database", user.getLogin());
|
||||||
|
updateUser(user.getFirstName(), user.getLastName(), user.getEmail(), user.getLangKey(), user.getImageUrl());
|
||||||
|
}
|
||||||
|
// no last updated info, blindly update
|
||||||
|
} else {
|
||||||
|
LOG.debug("Updating user '{}' in local database", user.getLogin());
|
||||||
|
updateUser(user.getFirstName(), user.getLastName(), user.getEmail(), user.getLangKey(), user.getImageUrl());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.debug("Saving user '{}' in local database", user.getLogin());
|
||||||
|
userRepository.save(user);
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user from an OAuth 2.0 login or resource server with JWT.
|
||||||
|
* Synchronizes the user in the local repository.
|
||||||
|
*
|
||||||
|
* @param authToken the authentication token.
|
||||||
|
* @return the user from the authentication.
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public AdminUserDTO getUserFromAuthentication(AbstractAuthenticationToken authToken) {
|
||||||
|
Map<String, Object> attributes;
|
||||||
|
if (authToken instanceof OAuth2AuthenticationToken) {
|
||||||
|
attributes = ((OAuth2AuthenticationToken) authToken).getPrincipal().getAttributes();
|
||||||
|
} else if (authToken instanceof JwtAuthenticationToken) {
|
||||||
|
attributes = ((JwtAuthenticationToken) authToken).getTokenAttributes();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("AuthenticationToken is not OAuth2 or JWT!");
|
||||||
|
}
|
||||||
|
User user = getUser(attributes);
|
||||||
|
user.setAuthorities(
|
||||||
|
authToken
|
||||||
|
.getAuthorities()
|
||||||
|
.stream()
|
||||||
|
.map(GrantedAuthority::getAuthority)
|
||||||
|
.map(authority -> {
|
||||||
|
Authority auth = new Authority();
|
||||||
|
auth.setName(authority);
|
||||||
|
return auth;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
);
|
||||||
|
|
||||||
|
return new AdminUserDTO(syncUserWithIdP(attributes, user));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static User getUser(Map<String, Object> details) {
|
||||||
|
User user = new User();
|
||||||
|
Boolean activated = Boolean.TRUE;
|
||||||
|
String sub = String.valueOf(details.get("sub"));
|
||||||
|
String username = null;
|
||||||
|
if (details.get("preferred_username") != null) {
|
||||||
|
username = ((String) details.get("preferred_username")).toLowerCase();
|
||||||
|
}
|
||||||
|
// handle resource server JWT, where sub claim is email and uid is ID
|
||||||
|
if (details.get("uid") != null) {
|
||||||
|
user.setId((String) details.get("uid"));
|
||||||
|
user.setLogin(sub);
|
||||||
|
} else {
|
||||||
|
user.setId(sub);
|
||||||
|
}
|
||||||
|
if (username != null) {
|
||||||
|
user.setLogin(username);
|
||||||
|
} else if (user.getLogin() == null) {
|
||||||
|
user.setLogin(user.getId());
|
||||||
|
}
|
||||||
|
if (details.get("given_name") != null) {
|
||||||
|
user.setFirstName((String) details.get("given_name"));
|
||||||
|
} else if (details.get("name") != null) {
|
||||||
|
user.setFirstName((String) details.get("name"));
|
||||||
|
}
|
||||||
|
if (details.get("family_name") != null) {
|
||||||
|
user.setLastName((String) details.get("family_name"));
|
||||||
|
}
|
||||||
|
if (details.get("email_verified") != null) {
|
||||||
|
activated = (Boolean) details.get("email_verified");
|
||||||
|
}
|
||||||
|
if (details.get("email") != null) {
|
||||||
|
user.setEmail(((String) details.get("email")).toLowerCase());
|
||||||
|
} else if (sub.contains("|") && (username != null && username.contains("@"))) {
|
||||||
|
// special handling for Auth0
|
||||||
|
user.setEmail(username);
|
||||||
|
} else {
|
||||||
|
user.setEmail(sub);
|
||||||
|
}
|
||||||
|
if (details.get("langKey") != null) {
|
||||||
|
user.setLangKey((String) details.get("langKey"));
|
||||||
|
} else if (details.get("locale") != null) {
|
||||||
|
// trim off country code if it exists
|
||||||
|
String locale = (String) details.get("locale");
|
||||||
|
if (locale.contains("_")) {
|
||||||
|
locale = locale.substring(0, locale.indexOf('_'));
|
||||||
|
} else if (locale.contains("-")) {
|
||||||
|
locale = locale.substring(0, locale.indexOf('-'));
|
||||||
|
}
|
||||||
|
user.setLangKey(locale.toLowerCase());
|
||||||
|
} else {
|
||||||
|
// set langKey to default if not specified by IdP
|
||||||
|
user.setLangKey(Constants.DEFAULT_LANGUAGE);
|
||||||
|
}
|
||||||
|
if (details.get("picture") != null) {
|
||||||
|
user.setImageUrl((String) details.get("picture"));
|
||||||
|
}
|
||||||
|
user.setActivated(activated);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
package com.sasiedzi.event.service.dto;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.config.Constants;
|
||||||
|
import com.sasiedzi.event.domain.Authority;
|
||||||
|
import com.sasiedzi.event.domain.User;
|
||||||
|
import jakarta.validation.constraints.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DTO representing a user, with his authorities.
|
||||||
|
*/
|
||||||
|
public class AdminUserDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||||
|
@Size(min = 1, max = 50)
|
||||||
|
private String login;
|
||||||
|
|
||||||
|
@Size(max = 50)
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
@Size(max = 50)
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
@Email
|
||||||
|
@Size(min = 5, max = 254)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Size(max = 256)
|
||||||
|
private String imageUrl;
|
||||||
|
|
||||||
|
private boolean activated = false;
|
||||||
|
|
||||||
|
@Size(min = 2, max = 10)
|
||||||
|
private String langKey;
|
||||||
|
|
||||||
|
private String createdBy;
|
||||||
|
|
||||||
|
private Instant createdDate;
|
||||||
|
|
||||||
|
private String lastModifiedBy;
|
||||||
|
|
||||||
|
private Instant lastModifiedDate;
|
||||||
|
|
||||||
|
private Set<String> authorities;
|
||||||
|
|
||||||
|
public AdminUserDTO() {
|
||||||
|
// Empty constructor needed for Jackson.
|
||||||
|
}
|
||||||
|
|
||||||
|
public AdminUserDTO(User user) {
|
||||||
|
this.id = user.getId();
|
||||||
|
this.login = user.getLogin();
|
||||||
|
this.firstName = user.getFirstName();
|
||||||
|
this.lastName = user.getLastName();
|
||||||
|
this.email = user.getEmail();
|
||||||
|
this.activated = user.isActivated();
|
||||||
|
this.imageUrl = user.getImageUrl();
|
||||||
|
this.langKey = user.getLangKey();
|
||||||
|
this.createdBy = user.getCreatedBy();
|
||||||
|
this.createdDate = user.getCreatedDate();
|
||||||
|
this.lastModifiedBy = user.getLastModifiedBy();
|
||||||
|
this.lastModifiedDate = user.getLastModifiedDate();
|
||||||
|
this.authorities = user.getAuthorities().stream().map(Authority::getName).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImageUrl() {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageUrl(String imageUrl) {
|
||||||
|
this.imageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActivated() {
|
||||||
|
return activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivated(boolean activated) {
|
||||||
|
this.activated = activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLangKey() {
|
||||||
|
return langKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLangKey(String langKey) {
|
||||||
|
this.langKey = langKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCreatedBy() {
|
||||||
|
return createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedBy(String createdBy) {
|
||||||
|
this.createdBy = createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getCreatedDate() {
|
||||||
|
return createdDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedDate(Instant createdDate) {
|
||||||
|
this.createdDate = createdDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastModifiedBy() {
|
||||||
|
return lastModifiedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastModifiedBy(String lastModifiedBy) {
|
||||||
|
this.lastModifiedBy = lastModifiedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getLastModifiedDate() {
|
||||||
|
return lastModifiedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastModifiedDate(Instant lastModifiedDate) {
|
||||||
|
this.lastModifiedDate = lastModifiedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getAuthorities() {
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthorities(Set<String> authorities) {
|
||||||
|
this.authorities = authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AdminUserDTO{" +
|
||||||
|
"login='" + login + '\'' +
|
||||||
|
", firstName='" + firstName + '\'' +
|
||||||
|
", lastName='" + lastName + '\'' +
|
||||||
|
", email='" + email + '\'' +
|
||||||
|
", imageUrl='" + imageUrl + '\'' +
|
||||||
|
", activated=" + activated +
|
||||||
|
", langKey='" + langKey + '\'' +
|
||||||
|
", createdBy=" + createdBy +
|
||||||
|
", createdDate=" + createdDate +
|
||||||
|
", lastModifiedBy='" + lastModifiedBy + '\'' +
|
||||||
|
", lastModifiedDate=" + lastModifiedDate +
|
||||||
|
", authorities=" + authorities +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.sasiedzi.event.service.dto;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.domain.User;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DTO representing a user, with only the public attributes.
|
||||||
|
*/
|
||||||
|
public class UserDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String login;
|
||||||
|
|
||||||
|
public UserDTO() {
|
||||||
|
// Empty constructor needed for Jackson.
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDTO(User user) {
|
||||||
|
this.id = user.getId();
|
||||||
|
// Customize it here if you need, or not, firstName/lastName/etc
|
||||||
|
this.login = user.getLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserDTO userDTO = (UserDTO) o;
|
||||||
|
if (userDTO.getId() == null || getId() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Objects.equals(getId(), userDTO.getId()) && Objects.equals(getLogin(), userDTO.getLogin());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(getId(), getLogin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UserDTO{" +
|
||||||
|
"id='" + id + '\'' +
|
||||||
|
", login='" + login + '\'' +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Data transfer objects for rest mapping.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.service.dto;
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package com.sasiedzi.event.service.mapper;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.domain.Authority;
|
||||||
|
import com.sasiedzi.event.domain.User;
|
||||||
|
import com.sasiedzi.event.service.dto.AdminUserDTO;
|
||||||
|
import com.sasiedzi.event.service.dto.UserDTO;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.mapstruct.BeanMapping;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Named;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapper for the entity {@link User} and its DTO called {@link UserDTO}.
|
||||||
|
*
|
||||||
|
* Normal mappers are generated using MapStruct, this one is hand-coded as MapStruct
|
||||||
|
* support is still in beta, and requires a manual step with an IDE.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class UserMapper {
|
||||||
|
|
||||||
|
public List<UserDTO> usersToUserDTOs(List<User> users) {
|
||||||
|
return users.stream().filter(Objects::nonNull).map(this::userToUserDTO).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDTO userToUserDTO(User user) {
|
||||||
|
return new UserDTO(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AdminUserDTO> usersToAdminUserDTOs(List<User> users) {
|
||||||
|
return users.stream().filter(Objects::nonNull).map(this::userToAdminUserDTO).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AdminUserDTO userToAdminUserDTO(User user) {
|
||||||
|
return new AdminUserDTO(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<User> userDTOsToUsers(List<AdminUserDTO> userDTOs) {
|
||||||
|
return userDTOs.stream().filter(Objects::nonNull).map(this::userDTOToUser).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public User userDTOToUser(AdminUserDTO userDTO) {
|
||||||
|
if (userDTO == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
User user = new User();
|
||||||
|
user.setId(userDTO.getId());
|
||||||
|
user.setLogin(userDTO.getLogin());
|
||||||
|
user.setFirstName(userDTO.getFirstName());
|
||||||
|
user.setLastName(userDTO.getLastName());
|
||||||
|
user.setEmail(userDTO.getEmail());
|
||||||
|
user.setImageUrl(userDTO.getImageUrl());
|
||||||
|
user.setCreatedBy(userDTO.getCreatedBy());
|
||||||
|
user.setCreatedDate(userDTO.getCreatedDate());
|
||||||
|
user.setLastModifiedBy(userDTO.getLastModifiedBy());
|
||||||
|
user.setLastModifiedDate(userDTO.getLastModifiedDate());
|
||||||
|
user.setActivated(userDTO.isActivated());
|
||||||
|
user.setLangKey(userDTO.getLangKey());
|
||||||
|
Set<Authority> authorities = this.authoritiesFromStrings(userDTO.getAuthorities());
|
||||||
|
user.setAuthorities(authorities);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Authority> authoritiesFromStrings(Set<String> authoritiesAsString) {
|
||||||
|
Set<Authority> authorities = new HashSet<>();
|
||||||
|
|
||||||
|
if (authoritiesAsString != null) {
|
||||||
|
authorities = authoritiesAsString
|
||||||
|
.stream()
|
||||||
|
.map(string -> {
|
||||||
|
Authority auth = new Authority();
|
||||||
|
auth.setName(string);
|
||||||
|
return auth;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User userFromId(String id) {
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
User user = new User();
|
||||||
|
user.setId(id);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named("id")
|
||||||
|
@BeanMapping(ignoreByDefault = true)
|
||||||
|
@Mapping(target = "id", source = "id")
|
||||||
|
public UserDTO toDtoId(User user) {
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
UserDTO userDto = new UserDTO();
|
||||||
|
userDto.setId(user.getId());
|
||||||
|
return userDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named("idSet")
|
||||||
|
@BeanMapping(ignoreByDefault = true)
|
||||||
|
@Mapping(target = "id", source = "id")
|
||||||
|
public Set<UserDTO> toDtoIdSet(Set<User> users) {
|
||||||
|
if (users == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<UserDTO> userSet = new HashSet<>();
|
||||||
|
for (User userEntity : users) {
|
||||||
|
userSet.add(this.toDtoId(userEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
return userSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named("login")
|
||||||
|
@BeanMapping(ignoreByDefault = true)
|
||||||
|
@Mapping(target = "id", source = "id")
|
||||||
|
@Mapping(target = "login", source = "login")
|
||||||
|
public UserDTO toDtoLogin(User user) {
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
UserDTO userDto = new UserDTO();
|
||||||
|
userDto.setId(user.getId());
|
||||||
|
userDto.setLogin(user.getLogin());
|
||||||
|
return userDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named("loginSet")
|
||||||
|
@BeanMapping(ignoreByDefault = true)
|
||||||
|
@Mapping(target = "id", source = "id")
|
||||||
|
@Mapping(target = "login", source = "login")
|
||||||
|
public Set<UserDTO> toDtoLoginSet(Set<User> users) {
|
||||||
|
if (users == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<UserDTO> userSet = new HashSet<>();
|
||||||
|
for (User userEntity : users) {
|
||||||
|
userSet.add(this.toDtoLogin(userEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
return userSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Data transfer objects mappers.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.service.mapper;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Service layer.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.service;
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.sasiedzi.event.web.filter;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||||
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
|
||||||
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
|
||||||
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
|
||||||
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||||
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||||
|
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||||
|
import org.springframework.security.web.RedirectStrategy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh oauth2 tokens.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class OAuth2RefreshTokensWebFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final OAuth2AuthorizedClientManager clientManager;
|
||||||
|
private final OAuth2AuthorizedClientRepository authorizedClientRepository;
|
||||||
|
private final OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||||
|
private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();
|
||||||
|
|
||||||
|
public OAuth2RefreshTokensWebFilter(
|
||||||
|
OAuth2AuthorizedClientManager clientManager,
|
||||||
|
OAuth2AuthorizedClientRepository authorizedClientRepository,
|
||||||
|
ClientRegistrationRepository clientRegistrationRepository
|
||||||
|
) {
|
||||||
|
this.clientManager = clientManager;
|
||||||
|
this.authorizedClientRepository = authorizedClientRepository;
|
||||||
|
this.authorizationRequestResolver = new DefaultOAuth2AuthorizationRequestResolver(
|
||||||
|
clientRegistrationRepository,
|
||||||
|
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if ((authentication instanceof OAuth2AuthenticationToken)) {
|
||||||
|
try {
|
||||||
|
OAuth2AuthorizedClient authorizedClient = authorizedClient((OAuth2AuthenticationToken) authentication);
|
||||||
|
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, authentication, request, response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);
|
||||||
|
if (authorizationRequest != null) {
|
||||||
|
this.authorizationRedirectStrategy.sendRedirect(request, response, authorizationRequest.getAuthorizationRequestUri());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2AuthorizedClient authorizedClient(OAuth2AuthenticationToken oauth2Authentication) {
|
||||||
|
String clientRegistrationId = oauth2Authentication.getAuthorizedClientRegistrationId();
|
||||||
|
OAuth2AuthorizeRequest request = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)
|
||||||
|
.principal(oauth2Authentication)
|
||||||
|
.build();
|
||||||
|
if (clientManager == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"No OAuth2AuthorizedClientManager bean was found. Did you include the " +
|
||||||
|
"org.springframework.boot:spring-boot-starter-oauth2-client dependency?"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return clientManager.authorize(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.sasiedzi.event.web.filter;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
public class SpaWebFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards any unmapped paths (except those containing a period) to the client {@code index.html}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
// Request URI includes the contextPath if any, removed it.
|
||||||
|
String path = request.getRequestURI().substring(request.getContextPath().length());
|
||||||
|
if (
|
||||||
|
!path.startsWith("/api") &&
|
||||||
|
!path.startsWith("/management") &&
|
||||||
|
!path.startsWith("/v3/api-docs") &&
|
||||||
|
!path.startsWith("/login") &&
|
||||||
|
!path.startsWith("/oauth2") &&
|
||||||
|
!path.contains(".") &&
|
||||||
|
path.matches("/(.*)")
|
||||||
|
) {
|
||||||
|
request.getRequestDispatcher("/index.html").forward(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Request chain filters.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.web.filter;
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.sasiedzi.event.web.rest;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.service.UserService;
|
||||||
|
import com.sasiedzi.event.service.dto.AdminUserDTO;
|
||||||
|
import java.security.Principal;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST controller for managing the current user's account.
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
public class AccountResource {
|
||||||
|
|
||||||
|
private static class AccountResourceException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private AccountResourceException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AccountResource.class);
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
public AccountResource(UserService userService) {
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code GET /account} : get the current user.
|
||||||
|
*
|
||||||
|
* @param principal the current user; resolves to {@code null} if not authenticated.
|
||||||
|
* @return the current user.
|
||||||
|
* @throws AccountResourceException {@code 500 (Internal Server Error)} if the user couldn't be returned.
|
||||||
|
*/
|
||||||
|
@GetMapping("/account")
|
||||||
|
public AdminUserDTO getAccount(Principal principal) {
|
||||||
|
if (principal instanceof AbstractAuthenticationToken) {
|
||||||
|
return userService.getUserFromAuthentication((AbstractAuthenticationToken) principal);
|
||||||
|
} else {
|
||||||
|
throw new AccountResourceException("User could not be found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code GET /authenticate} : check if the user is authenticated, and return its login.
|
||||||
|
*
|
||||||
|
* @param principal the authentication principal.
|
||||||
|
* @return the login if the user is authenticated.
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/authenticate", produces = MediaType.TEXT_PLAIN_VALUE)
|
||||||
|
public String isAuthenticated(Principal principal) {
|
||||||
|
LOG.debug("REST request to check if the current user is authenticated");
|
||||||
|
return principal == null ? null : principal.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.sasiedzi.event.web.rest;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource to return information about OIDC properties
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
public class AuthInfoResource {
|
||||||
|
|
||||||
|
@Value("${spring.security.oauth2.client.provider.oidc.issuer-uri:}")
|
||||||
|
private String issuer;
|
||||||
|
|
||||||
|
@Value("${spring.security.oauth2.client.registration.oidc.client-id:}")
|
||||||
|
private String clientId;
|
||||||
|
|
||||||
|
@GetMapping("/auth-info")
|
||||||
|
public AuthInfoVM getAuthInfo() {
|
||||||
|
return new AuthInfoVM(issuer, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthInfoVM {
|
||||||
|
|
||||||
|
private String issuer;
|
||||||
|
private String clientId;
|
||||||
|
|
||||||
|
AuthInfoVM(String issuer, String clientId) {
|
||||||
|
this.issuer = issuer;
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIssuer() {
|
||||||
|
return this.issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIssuer(String issuer) {
|
||||||
|
this.issuer = issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientId() {
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientId(String clientId) {
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package com.sasiedzi.event.web.rest;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.domain.Authority;
|
||||||
|
import com.sasiedzi.event.repository.AuthorityRepository;
|
||||||
|
import com.sasiedzi.event.web.rest.errors.BadRequestAlertException;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import tech.jhipster.web.util.HeaderUtil;
|
||||||
|
import tech.jhipster.web.util.ResponseUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST controller for managing {@link com.sasiedzi.event.domain.Authority}.
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/authorities")
|
||||||
|
@Transactional
|
||||||
|
public class AuthorityResource {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AuthorityResource.class);
|
||||||
|
|
||||||
|
private static final String ENTITY_NAME = "adminAuthority";
|
||||||
|
|
||||||
|
@Value("${jhipster.clientApp.name}")
|
||||||
|
private String applicationName;
|
||||||
|
|
||||||
|
private final AuthorityRepository authorityRepository;
|
||||||
|
|
||||||
|
public AuthorityResource(AuthorityRepository authorityRepository) {
|
||||||
|
this.authorityRepository = authorityRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code POST /authorities} : Create a new authority.
|
||||||
|
*
|
||||||
|
* @param authority the authority to create.
|
||||||
|
* @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new authority, or with status {@code 400 (Bad Request)} if the authority has already an ID.
|
||||||
|
* @throws URISyntaxException if the Location URI syntax is incorrect.
|
||||||
|
*/
|
||||||
|
@PostMapping("")
|
||||||
|
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
|
||||||
|
public ResponseEntity<Authority> createAuthority(@Valid @RequestBody Authority authority) throws URISyntaxException {
|
||||||
|
LOG.debug("REST request to save Authority : {}", authority);
|
||||||
|
if (authorityRepository.existsById(authority.getName())) {
|
||||||
|
throw new BadRequestAlertException("authority already exists", ENTITY_NAME, "idexists");
|
||||||
|
}
|
||||||
|
authority = authorityRepository.save(authority);
|
||||||
|
return ResponseEntity.created(new URI("/api/authorities/" + authority.getName()))
|
||||||
|
.headers(HeaderUtil.createEntityCreationAlert(applicationName, false, ENTITY_NAME, authority.getName()))
|
||||||
|
.body(authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code GET /authorities} : get all the authorities.
|
||||||
|
*
|
||||||
|
* @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of authorities in body.
|
||||||
|
*/
|
||||||
|
@GetMapping("")
|
||||||
|
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
|
||||||
|
public List<Authority> getAllAuthorities() {
|
||||||
|
LOG.debug("REST request to get all Authorities");
|
||||||
|
return authorityRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code GET /authorities/:id} : get the "id" authority.
|
||||||
|
*
|
||||||
|
* @param id the id of the authority to retrieve.
|
||||||
|
* @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the authority, or with status {@code 404 (Not Found)}.
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
|
||||||
|
public ResponseEntity<Authority> getAuthority(@PathVariable("id") String id) {
|
||||||
|
LOG.debug("REST request to get Authority : {}", id);
|
||||||
|
Optional<Authority> authority = authorityRepository.findById(id);
|
||||||
|
return ResponseUtil.wrapOrNotFound(authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code DELETE /authorities/:id} : delete the "id" authority.
|
||||||
|
*
|
||||||
|
* @param id the id of the authority to delete.
|
||||||
|
* @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}.
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
|
||||||
|
public ResponseEntity<Void> deleteAuthority(@PathVariable("id") String id) {
|
||||||
|
LOG.debug("REST request to delete Authority : {}", id);
|
||||||
|
authorityRepository.deleteById(id);
|
||||||
|
return ResponseEntity.noContent().headers(HeaderUtil.createEntityDeletionAlert(applicationName, false, ENTITY_NAME, id)).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.sasiedzi.event.web.rest;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST controller for managing global OIDC logout.
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class LogoutResource {
|
||||||
|
|
||||||
|
private final ClientRegistration registration;
|
||||||
|
|
||||||
|
public LogoutResource(ClientRegistrationRepository registrations) {
|
||||||
|
this.registration = registrations.findByRegistrationId("oidc");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code POST /api/logout} : logout the current user.
|
||||||
|
*
|
||||||
|
* @param request the {@link HttpServletRequest}.
|
||||||
|
* @param idToken the ID token.
|
||||||
|
* @return the {@link ResponseEntity} with status {@code 200 (OK)} and a body with a global logout URL.
|
||||||
|
*/
|
||||||
|
@PostMapping("/api/logout")
|
||||||
|
public ResponseEntity<?> logout(HttpServletRequest request, @AuthenticationPrincipal(expression = "idToken") OidcIdToken idToken) {
|
||||||
|
StringBuilder logoutUrl = new StringBuilder();
|
||||||
|
|
||||||
|
logoutUrl.append(this.registration.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint").toString());
|
||||||
|
|
||||||
|
String originUrl = request.getHeader(HttpHeaders.ORIGIN);
|
||||||
|
|
||||||
|
logoutUrl.append("?id_token_hint=").append(idToken.getTokenValue()).append("&post_logout_redirect_uri=").append(originUrl);
|
||||||
|
|
||||||
|
request.getSession().invalidate();
|
||||||
|
return ResponseEntity.ok().body(Map.of("logoutUrl", logoutUrl.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.sasiedzi.event.web.rest;
|
||||||
|
|
||||||
|
import com.sasiedzi.event.service.UserService;
|
||||||
|
import com.sasiedzi.event.service.dto.UserDTO;
|
||||||
|
import java.util.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||||
|
import tech.jhipster.web.util.PaginationUtil;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
public class PublicUserResource {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(PublicUserResource.class);
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
public PublicUserResource(UserService userService) {
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code GET /users} : get all users with only public information - calling this method is allowed for anyone.
|
||||||
|
*
|
||||||
|
* @param pageable the pagination information.
|
||||||
|
* @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body all users.
|
||||||
|
*/
|
||||||
|
@GetMapping("/users")
|
||||||
|
public ResponseEntity<List<UserDTO>> getAllPublicUsers(@org.springdoc.core.annotations.ParameterObject Pageable pageable) {
|
||||||
|
LOG.debug("REST request to get all public User names");
|
||||||
|
|
||||||
|
final Page<UserDTO> page = userService.getAllPublicUsers(pageable);
|
||||||
|
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
|
||||||
|
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.sasiedzi.event.web.rest.errors;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.ErrorResponseException;
|
||||||
|
import tech.jhipster.web.rest.errors.ProblemDetailWithCause;
|
||||||
|
import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder;
|
||||||
|
|
||||||
|
@SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep
|
||||||
|
public class BadRequestAlertException extends ErrorResponseException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final String entityName;
|
||||||
|
|
||||||
|
private final String errorKey;
|
||||||
|
|
||||||
|
public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
|
||||||
|
this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
|
||||||
|
super(
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
ProblemDetailWithCauseBuilder.instance()
|
||||||
|
.withStatus(HttpStatus.BAD_REQUEST.value())
|
||||||
|
.withType(type)
|
||||||
|
.withTitle(defaultMessage)
|
||||||
|
.withProperty("message", "error." + errorKey)
|
||||||
|
.withProperty("params", entityName)
|
||||||
|
.build(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
this.entityName = entityName;
|
||||||
|
this.errorKey = errorKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityName() {
|
||||||
|
return entityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorKey() {
|
||||||
|
return errorKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProblemDetailWithCause getProblemDetailWithCause() {
|
||||||
|
return (ProblemDetailWithCause) this.getBody();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.sasiedzi.event.web.rest.errors;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
public final class ErrorConstants {
|
||||||
|
|
||||||
|
public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure";
|
||||||
|
public static final String ERR_VALIDATION = "error.validation";
|
||||||
|
public static final String PROBLEM_BASE_URL = "https://www.jhipster.tech/problem";
|
||||||
|
public static final URI DEFAULT_TYPE = URI.create(PROBLEM_BASE_URL + "/problem-with-message");
|
||||||
|
public static final URI CONSTRAINT_VIOLATION_TYPE = URI.create(PROBLEM_BASE_URL + "/constraint-violation");
|
||||||
|
|
||||||
|
private ErrorConstants() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
package com.sasiedzi.event.web.rest.errors;
|
||||||
|
|
||||||
|
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.HttpStatusCode;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.converter.HttpMessageConversionException;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.web.ErrorResponse;
|
||||||
|
import org.springframework.web.ErrorResponseException;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||||
|
import tech.jhipster.config.JHipsterConstants;
|
||||||
|
import tech.jhipster.web.rest.errors.ProblemDetailWithCause;
|
||||||
|
import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder;
|
||||||
|
import tech.jhipster.web.util.HeaderUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller advice to translate the server side exceptions to client-friendly json structures.
|
||||||
|
* The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807).
|
||||||
|
*/
|
||||||
|
@ControllerAdvice
|
||||||
|
public class ExceptionTranslator extends ResponseEntityExceptionHandler {
|
||||||
|
|
||||||
|
private static final String FIELD_ERRORS_KEY = "fieldErrors";
|
||||||
|
private static final String MESSAGE_KEY = "message";
|
||||||
|
private static final String PATH_KEY = "path";
|
||||||
|
private static final boolean CASUAL_CHAIN_ENABLED = false;
|
||||||
|
|
||||||
|
@Value("${jhipster.clientApp.name}")
|
||||||
|
private String applicationName;
|
||||||
|
|
||||||
|
private final Environment env;
|
||||||
|
|
||||||
|
public ExceptionTranslator(Environment env) {
|
||||||
|
this.env = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler
|
||||||
|
public ResponseEntity<Object> handleAnyException(Throwable ex, NativeWebRequest request) {
|
||||||
|
ProblemDetailWithCause pdCause = wrapAndCustomizeProblem(ex, request);
|
||||||
|
return handleExceptionInternal((Exception) ex, pdCause, buildHeaders(ex), HttpStatusCode.valueOf(pdCause.getStatus()), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleExceptionInternal(
|
||||||
|
Exception ex,
|
||||||
|
@Nullable Object body,
|
||||||
|
HttpHeaders headers,
|
||||||
|
HttpStatusCode statusCode,
|
||||||
|
WebRequest request
|
||||||
|
) {
|
||||||
|
body = body == null ? wrapAndCustomizeProblem((Throwable) ex, (NativeWebRequest) request) : body;
|
||||||
|
return super.handleExceptionInternal(ex, body, headers, statusCode, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProblemDetailWithCause wrapAndCustomizeProblem(Throwable ex, NativeWebRequest request) {
|
||||||
|
return customizeProblem(getProblemDetailWithCause(ex), ex, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProblemDetailWithCause getProblemDetailWithCause(Throwable ex) {
|
||||||
|
if (
|
||||||
|
ex instanceof ErrorResponseException exp && exp.getBody() instanceof ProblemDetailWithCause problemDetailWithCause
|
||||||
|
) return problemDetailWithCause;
|
||||||
|
return ProblemDetailWithCauseBuilder.instance().withStatus(toStatus(ex).value()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProblemDetailWithCause customizeProblem(ProblemDetailWithCause problem, Throwable err, NativeWebRequest request) {
|
||||||
|
if (problem.getStatus() <= 0) problem.setStatus(toStatus(err));
|
||||||
|
|
||||||
|
if (problem.getType() == null || problem.getType().equals(URI.create("about:blank"))) problem.setType(getMappedType(err));
|
||||||
|
|
||||||
|
// higher precedence to Custom/ResponseStatus types
|
||||||
|
String title = extractTitle(err, problem.getStatus());
|
||||||
|
String problemTitle = problem.getTitle();
|
||||||
|
if (problemTitle == null || !problemTitle.equals(title)) {
|
||||||
|
problem.setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (problem.getDetail() == null) {
|
||||||
|
// higher precedence to cause
|
||||||
|
problem.setDetail(getCustomizedErrorDetails(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> problemProperties = problem.getProperties();
|
||||||
|
if (problemProperties == null || !problemProperties.containsKey(MESSAGE_KEY)) problem.setProperty(
|
||||||
|
MESSAGE_KEY,
|
||||||
|
getMappedMessageKey(err) != null ? getMappedMessageKey(err) : "error.http." + problem.getStatus()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (problemProperties == null || !problemProperties.containsKey(PATH_KEY)) problem.setProperty(PATH_KEY, getPathValue(request));
|
||||||
|
|
||||||
|
if (
|
||||||
|
(err instanceof MethodArgumentNotValidException fieldException) &&
|
||||||
|
(problemProperties == null || !problemProperties.containsKey(FIELD_ERRORS_KEY))
|
||||||
|
) problem.setProperty(FIELD_ERRORS_KEY, getFieldErrors(fieldException));
|
||||||
|
|
||||||
|
problem.setCause(buildCause(err.getCause(), request).orElse(null));
|
||||||
|
|
||||||
|
return problem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractTitle(Throwable err, int statusCode) {
|
||||||
|
return getCustomizedTitle(err) != null ? getCustomizedTitle(err) : extractTitleForResponseStatus(err, statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<FieldErrorVM> getFieldErrors(MethodArgumentNotValidException ex) {
|
||||||
|
return ex
|
||||||
|
.getBindingResult()
|
||||||
|
.getFieldErrors()
|
||||||
|
.stream()
|
||||||
|
.map(f ->
|
||||||
|
new FieldErrorVM(
|
||||||
|
f.getObjectName().replaceFirst("DTO$", ""),
|
||||||
|
f.getField(),
|
||||||
|
StringUtils.isNotBlank(f.getDefaultMessage()) ? f.getDefaultMessage() : f.getCode()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractTitleForResponseStatus(Throwable err, int statusCode) {
|
||||||
|
ResponseStatus specialStatus = extractResponseStatus(err);
|
||||||
|
return specialStatus == null ? HttpStatus.valueOf(statusCode).getReasonPhrase() : specialStatus.reason();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractURI(NativeWebRequest request) {
|
||||||
|
HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class);
|
||||||
|
return nativeRequest != null ? nativeRequest.getRequestURI() : StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpStatus toStatus(final Throwable throwable) {
|
||||||
|
// Let the ErrorResponse take this responsibility
|
||||||
|
if (throwable instanceof ErrorResponse err) return HttpStatus.valueOf(err.getBody().getStatus());
|
||||||
|
|
||||||
|
return Optional.ofNullable(getMappedStatus(throwable)).orElse(
|
||||||
|
Optional.ofNullable(resolveResponseStatus(throwable)).map(ResponseStatus::value).orElse(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseStatus extractResponseStatus(final Throwable throwable) {
|
||||||
|
return Optional.ofNullable(resolveResponseStatus(throwable)).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseStatus resolveResponseStatus(final Throwable type) {
|
||||||
|
final ResponseStatus candidate = findMergedAnnotation(type.getClass(), ResponseStatus.class);
|
||||||
|
return candidate == null && type.getCause() != null ? resolveResponseStatus(type.getCause()) : candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private URI getMappedType(Throwable err) {
|
||||||
|
if (err instanceof MethodArgumentNotValidException) return ErrorConstants.CONSTRAINT_VIOLATION_TYPE;
|
||||||
|
return ErrorConstants.DEFAULT_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMappedMessageKey(Throwable err) {
|
||||||
|
if (err instanceof MethodArgumentNotValidException) {
|
||||||
|
return ErrorConstants.ERR_VALIDATION;
|
||||||
|
} else if (err instanceof ConcurrencyFailureException || err.getCause() instanceof ConcurrencyFailureException) {
|
||||||
|
return ErrorConstants.ERR_CONCURRENCY_FAILURE;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCustomizedTitle(Throwable err) {
|
||||||
|
if (err instanceof MethodArgumentNotValidException) return "Method argument not valid";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCustomizedErrorDetails(Throwable err) {
|
||||||
|
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
|
||||||
|
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
|
||||||
|
if (err instanceof HttpMessageConversionException) return "Unable to convert http message";
|
||||||
|
if (err instanceof DataAccessException) return "Failure during data access";
|
||||||
|
if (containsPackageName(err.getMessage())) return "Unexpected runtime exception";
|
||||||
|
}
|
||||||
|
return err.getCause() != null ? err.getCause().getMessage() : err.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpStatus getMappedStatus(Throwable err) {
|
||||||
|
// Where we disagree with Spring defaults
|
||||||
|
if (err instanceof AccessDeniedException) return HttpStatus.FORBIDDEN;
|
||||||
|
if (err instanceof ConcurrencyFailureException) return HttpStatus.CONFLICT;
|
||||||
|
if (err instanceof BadCredentialsException) return HttpStatus.UNAUTHORIZED;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private URI getPathValue(NativeWebRequest request) {
|
||||||
|
if (request == null) return URI.create("about:blank");
|
||||||
|
return URI.create(extractURI(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpHeaders buildHeaders(Throwable err) {
|
||||||
|
return err instanceof BadRequestAlertException badRequestAlertException
|
||||||
|
? HeaderUtil.createFailureAlert(
|
||||||
|
applicationName,
|
||||||
|
true,
|
||||||
|
badRequestAlertException.getEntityName(),
|
||||||
|
badRequestAlertException.getErrorKey(),
|
||||||
|
badRequestAlertException.getMessage()
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ProblemDetailWithCause> buildCause(final Throwable throwable, NativeWebRequest request) {
|
||||||
|
if (throwable != null && isCasualChainEnabled()) {
|
||||||
|
return Optional.of(customizeProblem(getProblemDetailWithCause(throwable), throwable, request));
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCasualChainEnabled() {
|
||||||
|
// Customize as per the needs
|
||||||
|
return CASUAL_CHAIN_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsPackageName(String message) {
|
||||||
|
// This list is for sure not complete
|
||||||
|
return StringUtils.containsAny(message, "org.", "java.", "net.", "jakarta.", "javax.", "com.", "io.", "de.", "com.sasiedzi.event");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.sasiedzi.event.web.rest.errors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class FieldErrorVM implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final String objectName;
|
||||||
|
|
||||||
|
private final String field;
|
||||||
|
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
public FieldErrorVM(String dto, String field, String message) {
|
||||||
|
this.objectName = dto;
|
||||||
|
this.field = field;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getObjectName() {
|
||||||
|
return objectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getField() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Rest layer error handling.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.web.rest.errors;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Rest layer.
|
||||||
|
*/
|
||||||
|
package com.sasiedzi.event.web.rest;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
${AnsiColor.GREEN} ██╗${AnsiColor.BLUE} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ████${AnsiColor.GREEN}███╗
|
||||||
|
${AnsiColor.GREEN} ██║ ██║ ${AnsiColor.BLUE} ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔══${AnsiColor.GREEN}═══╝ ██╔═══██╗
|
||||||
|
${AnsiColor.GREEN} ██║ ████████║${AnsiColor.BLUE} ██║ ███████╔╝ ╚█████╗ ██║ ${AnsiColor.GREEN} ██████╗ ███████╔╝
|
||||||
|
${AnsiColor.GREEN}██╗ ██║ ██╔═══██║ ██║ ${AnsiColor.BLUE} ██╔════╝ ╚═══██╗ ${AnsiColor.GREEN}██║ ██╔═══╝ ██╔══██║
|
||||||
|
${AnsiColor.GREEN}╚██████╔╝ ██║ ██║ ████████╗ ██║ ${AnsiColor.BLUE} ██████${AnsiColor.GREEN}╔╝ ██║ ████████╗ ██║ ╚██╗
|
||||||
|
${AnsiColor.GREEN} ╚═════╝ ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ${AnsiColor.BLUE} ╚═${AnsiColor.GREEN}════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝
|
||||||
|
|
||||||
|
${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: Startup profile(s) ${spring.profiles.active} ::
|
||||||
|
:: https://www.jhipster.tech ::${AnsiColor.DEFAULT}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# ===================================================================
|
||||||
|
# Spring Boot configuration for the "dev" profile.
|
||||||
|
#
|
||||||
|
# This configuration overrides the application.yml file.
|
||||||
|
#
|
||||||
|
# More information on profiles: https://www.jhipster.tech/profiles/
|
||||||
|
# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Standard Spring Boot properties.
|
||||||
|
# Full reference is available at:
|
||||||
|
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
ROOT: DEBUG
|
||||||
|
tech.jhipster: DEBUG
|
||||||
|
org.hibernate.SQL: DEBUG
|
||||||
|
com.sasiedzi.event: DEBUG
|
||||||
|
|
||||||
|
spring:
|
||||||
|
devtools:
|
||||||
|
restart:
|
||||||
|
enabled: true
|
||||||
|
additional-exclude: static/**
|
||||||
|
livereload:
|
||||||
|
enabled: false # we use Webpack dev server + BrowserSync for livereload
|
||||||
|
jackson:
|
||||||
|
serialization:
|
||||||
|
indent-output: true
|
||||||
|
datasource:
|
||||||
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
|
url: jdbc:postgresql://localhost:5432/sasiedzi
|
||||||
|
username: sasiedzi
|
||||||
|
password: password
|
||||||
|
hikari:
|
||||||
|
poolName: Hikari
|
||||||
|
auto-commit: false
|
||||||
|
liquibase:
|
||||||
|
# Remove 'faker' if you do not want the sample data to be loaded automatically
|
||||||
|
contexts: dev, faker
|
||||||
|
messages:
|
||||||
|
cache-duration: PT1S # 1 second, see the ISO 8601 standard
|
||||||
|
thymeleaf:
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
# make sure requests the proxy uri instead of the server one
|
||||||
|
forward-headers-strategy: native
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# JHipster specific properties
|
||||||
|
#
|
||||||
|
# Full reference is available at: https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
jhipster:
|
||||||
|
# CORS is only enabled by default with the "dev" profile
|
||||||
|
cors:
|
||||||
|
# Allow Ionic for JHipster by default (* no longer allowed in Spring Boot 2.4+)
|
||||||
|
allowed-origins: 'http://localhost:8100,https://localhost:8100,http://localhost:9000,https://localhost:9000,http://localhost:9060,https://localhost:9060'
|
||||||
|
# Enable CORS when running in GitHub Codespaces
|
||||||
|
allowed-origin-patterns: 'https://*.githubpreview.dev'
|
||||||
|
allowed-methods: '*'
|
||||||
|
allowed-headers: '*'
|
||||||
|
exposed-headers: 'Authorization,Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params'
|
||||||
|
allow-credentials: true
|
||||||
|
max-age: 1800
|
||||||
|
logging:
|
||||||
|
use-json-format: false # By default, logs are not in Json format
|
||||||
|
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
|
||||||
|
enabled: false
|
||||||
|
host: localhost
|
||||||
|
port: 5000
|
||||||
|
ring-buffer-size: 512
|
||||||
|
# ===================================================================
|
||||||
|
# Application specific properties
|
||||||
|
# Add your own application properties here, see the ApplicationProperties class
|
||||||
|
# to have type-safe configuration, like in the JHipsterProperties above
|
||||||
|
#
|
||||||
|
# More documentation is available at:
|
||||||
|
# https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# application:
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# ===================================================================
|
||||||
|
# Spring Boot configuration for the "prod" profile.
|
||||||
|
#
|
||||||
|
# This configuration overrides the application.yml file.
|
||||||
|
#
|
||||||
|
# More information on profiles: https://www.jhipster.tech/profiles/
|
||||||
|
# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Standard Spring Boot properties.
|
||||||
|
# Full reference is available at:
|
||||||
|
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
ROOT: INFO
|
||||||
|
tech.jhipster: INFO
|
||||||
|
com.sasiedzi.event: INFO
|
||||||
|
|
||||||
|
management:
|
||||||
|
prometheus:
|
||||||
|
metrics:
|
||||||
|
export:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
spring:
|
||||||
|
devtools:
|
||||||
|
restart:
|
||||||
|
enabled: false
|
||||||
|
livereload:
|
||||||
|
enabled: false
|
||||||
|
datasource:
|
||||||
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
|
url: jdbc:postgresql://localhost:5432/sasiedzi
|
||||||
|
username: sasiedzi
|
||||||
|
password: password
|
||||||
|
hikari:
|
||||||
|
poolName: Hikari
|
||||||
|
auto-commit: false
|
||||||
|
# Replace by 'prod, faker' to add the faker context and have sample data loaded in production
|
||||||
|
liquibase:
|
||||||
|
contexts: prod
|
||||||
|
thymeleaf:
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# To enable TLS in production, generate a certificate using:
|
||||||
|
# keytool -genkey -alias sasiedzi -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
|
||||||
|
#
|
||||||
|
# You can also use Let's Encrypt:
|
||||||
|
# See details in topic "Create a Java Keystore (.JKS) from Let's Encrypt Certificates" on https://maximilian-boehm.com/en-gb/blog
|
||||||
|
#
|
||||||
|
# Then, modify the server.ssl properties so your "server" configuration looks like:
|
||||||
|
#
|
||||||
|
# server:
|
||||||
|
# port: 443
|
||||||
|
# ssl:
|
||||||
|
# key-store: classpath:config/tls/keystore.p12
|
||||||
|
# key-store-password: password
|
||||||
|
# key-store-type: PKCS12
|
||||||
|
# key-alias: selfsigned
|
||||||
|
# # The ciphers suite enforce the security by deactivating some old and deprecated SSL cipher, this list was tested against SSL Labs (https://www.ssllabs.com/ssltest/)
|
||||||
|
# ciphers: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
|
||||||
|
# ===================================================================
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
shutdown: graceful # see https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-graceful-shutdown
|
||||||
|
compression:
|
||||||
|
enabled: true
|
||||||
|
mime-types: text/html,text/xml,text/plain,text/css,application/javascript,application/json,image/svg+xml
|
||||||
|
min-response-size: 1024
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# JHipster specific properties
|
||||||
|
#
|
||||||
|
# Full reference is available at: https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
jhipster:
|
||||||
|
http:
|
||||||
|
cache: # Used by the CachingHttpHeadersFilter
|
||||||
|
timeToLiveInDays: 1461
|
||||||
|
logging:
|
||||||
|
use-json-format: false # By default, logs are not in Json format
|
||||||
|
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
|
||||||
|
enabled: false
|
||||||
|
host: localhost
|
||||||
|
port: 5000
|
||||||
|
ring-buffer-size: 512
|
||||||
|
# ===================================================================
|
||||||
|
# Application specific properties
|
||||||
|
# Add your own application properties here, see the ApplicationProperties class
|
||||||
|
# to have type-safe configuration, like in the JHipsterProperties above
|
||||||
|
#
|
||||||
|
# More documentation is available at:
|
||||||
|
# https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# application:
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# ===================================================================
|
||||||
|
# Activate this profile to enable TLS and HTTP/2.
|
||||||
|
#
|
||||||
|
# JHipster has generated a self-signed certificate, which will be used to encrypt traffic.
|
||||||
|
# As your browser will not understand this certificate, you will need to import it.
|
||||||
|
#
|
||||||
|
# Another (easiest) solution with Chrome is to enable the "allow-insecure-localhost" flag
|
||||||
|
# at chrome://flags/#allow-insecure-localhost
|
||||||
|
# ===================================================================
|
||||||
|
server:
|
||||||
|
ssl:
|
||||||
|
key-store: classpath:config/tls/keystore.p12
|
||||||
|
key-store-password: password
|
||||||
|
key-store-type: PKCS12
|
||||||
|
key-alias: selfsigned
|
||||||
|
ciphers: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
|
||||||
|
enabled-protocols: TLSv1.2
|
||||||
|
http2:
|
||||||
|
enabled: true
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
# ===================================================================
|
||||||
|
# Spring Boot configuration.
|
||||||
|
#
|
||||||
|
# This configuration will be overridden by the Spring profile you use,
|
||||||
|
# for example application-dev.yml if you use the "dev" profile.
|
||||||
|
#
|
||||||
|
# More information on profiles: https://www.jhipster.tech/profiles/
|
||||||
|
# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Standard Spring Boot properties.
|
||||||
|
# Full reference is available at:
|
||||||
|
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
---
|
||||||
|
# Conditionally disable springdoc on missing api-docs profile
|
||||||
|
spring:
|
||||||
|
config:
|
||||||
|
activate:
|
||||||
|
on-profile: '!api-docs'
|
||||||
|
springdoc:
|
||||||
|
api-docs:
|
||||||
|
enabled: false
|
||||||
|
---
|
||||||
|
management:
|
||||||
|
endpoints:
|
||||||
|
web:
|
||||||
|
base-path: /management
|
||||||
|
exposure:
|
||||||
|
include:
|
||||||
|
- configprops
|
||||||
|
- env
|
||||||
|
- health
|
||||||
|
- info
|
||||||
|
- jhimetrics
|
||||||
|
- jhiopenapigroups
|
||||||
|
- logfile
|
||||||
|
- loggers
|
||||||
|
- prometheus
|
||||||
|
- threaddump
|
||||||
|
- liquibase
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
show-details: when_authorized
|
||||||
|
roles: 'ROLE_ADMIN'
|
||||||
|
probes:
|
||||||
|
enabled: true
|
||||||
|
group:
|
||||||
|
liveness:
|
||||||
|
include: livenessState
|
||||||
|
readiness:
|
||||||
|
include: readinessState,db
|
||||||
|
jhimetrics:
|
||||||
|
enabled: true
|
||||||
|
info:
|
||||||
|
git:
|
||||||
|
mode: full
|
||||||
|
env:
|
||||||
|
enabled: true
|
||||||
|
health:
|
||||||
|
mail:
|
||||||
|
enabled: false # When using the MailService, configure an SMTP server and set this to true
|
||||||
|
prometheus:
|
||||||
|
metrics:
|
||||||
|
export:
|
||||||
|
enabled: true
|
||||||
|
step: 60
|
||||||
|
observations:
|
||||||
|
key-values:
|
||||||
|
application: ${spring.application.name}
|
||||||
|
metrics:
|
||||||
|
enable:
|
||||||
|
http: true
|
||||||
|
jvm: true
|
||||||
|
logback: true
|
||||||
|
process: true
|
||||||
|
system: true
|
||||||
|
distribution:
|
||||||
|
percentiles-histogram:
|
||||||
|
all: true
|
||||||
|
percentiles:
|
||||||
|
all: 0, 0.5, 0.75, 0.95, 0.99, 1.0
|
||||||
|
data:
|
||||||
|
repository:
|
||||||
|
autotime:
|
||||||
|
enabled: true
|
||||||
|
tags:
|
||||||
|
application: ${spring.application.name}
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: sasiedzi
|
||||||
|
docker:
|
||||||
|
compose:
|
||||||
|
enabled: true
|
||||||
|
lifecycle-management: start-only
|
||||||
|
file: src/main/docker/services.yml
|
||||||
|
profiles:
|
||||||
|
# The commented value for `active` can be replaced with valid Spring profiles to load.
|
||||||
|
# Otherwise, it will be filled in by maven when building the JAR file
|
||||||
|
# Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS`
|
||||||
|
active: '@spring.profiles.active@'
|
||||||
|
group:
|
||||||
|
dev:
|
||||||
|
- dev
|
||||||
|
- api-docs
|
||||||
|
# Uncomment to activate TLS for the dev profile
|
||||||
|
#- tls
|
||||||
|
jmx:
|
||||||
|
enabled: false
|
||||||
|
data:
|
||||||
|
jpa:
|
||||||
|
repositories:
|
||||||
|
bootstrap-mode: deferred
|
||||||
|
jpa:
|
||||||
|
open-in-view: false
|
||||||
|
properties:
|
||||||
|
hibernate.jdbc.time_zone: UTC
|
||||||
|
hibernate.timezone.default_storage: NORMALIZE
|
||||||
|
hibernate.type.preferred_instant_jdbc_type: TIMESTAMP
|
||||||
|
hibernate.id.new_generator_mappings: true
|
||||||
|
hibernate.connection.provider_disables_autocommit: true
|
||||||
|
hibernate.cache.use_second_level_cache: false
|
||||||
|
hibernate.cache.use_query_cache: false
|
||||||
|
hibernate.generate_statistics: false
|
||||||
|
# modify batch size as necessary
|
||||||
|
hibernate.jdbc.batch_size: 25
|
||||||
|
hibernate.order_inserts: true
|
||||||
|
hibernate.order_updates: true
|
||||||
|
hibernate.query.fail_on_pagination_over_collection_fetch: true
|
||||||
|
hibernate.query.in_clause_parameter_padding: true
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: none
|
||||||
|
naming:
|
||||||
|
physical-strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
|
||||||
|
implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
|
||||||
|
messages:
|
||||||
|
basename: i18n/messages
|
||||||
|
main:
|
||||||
|
allow-bean-definition-overriding: true
|
||||||
|
mvc:
|
||||||
|
problemdetails:
|
||||||
|
enabled: true
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
client:
|
||||||
|
provider:
|
||||||
|
oidc:
|
||||||
|
issuer-uri: http://localhost:9080/realms/jhipster
|
||||||
|
registration:
|
||||||
|
oidc:
|
||||||
|
client-id: web_app
|
||||||
|
client-secret: web_app
|
||||||
|
scope: openid, profile, email, offline_access # last one for refresh tokens
|
||||||
|
task:
|
||||||
|
execution:
|
||||||
|
thread-name-prefix: sasiedzi-task-
|
||||||
|
pool:
|
||||||
|
core-size: 2
|
||||||
|
max-size: 50
|
||||||
|
queue-capacity: 10000
|
||||||
|
scheduling:
|
||||||
|
thread-name-prefix: sasiedzi-scheduling-
|
||||||
|
pool:
|
||||||
|
size: 2
|
||||||
|
thymeleaf:
|
||||||
|
mode: HTML
|
||||||
|
output:
|
||||||
|
ansi:
|
||||||
|
console-available: true
|
||||||
|
|
||||||
|
server:
|
||||||
|
servlet:
|
||||||
|
session:
|
||||||
|
cookie:
|
||||||
|
http-only: true
|
||||||
|
|
||||||
|
springdoc:
|
||||||
|
show-actuator: true
|
||||||
|
|
||||||
|
# Properties to be exposed on the /info management endpoint
|
||||||
|
info:
|
||||||
|
# Comma separated list of profiles that will trigger the ribbon to show
|
||||||
|
display-ribbon-on-profiles: 'dev'
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# JHipster specific properties
|
||||||
|
#
|
||||||
|
# Full reference is available at: https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
jhipster:
|
||||||
|
clientApp:
|
||||||
|
name: 'sasiedziApp'
|
||||||
|
# By default CORS is disabled. Uncomment to enable.
|
||||||
|
# cors:
|
||||||
|
# allowed-origins: "http://localhost:8100,http://localhost:9000"
|
||||||
|
# allowed-methods: "*"
|
||||||
|
# allowed-headers: "*"
|
||||||
|
# exposed-headers: "Authorization,Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params"
|
||||||
|
# allow-credentials: true
|
||||||
|
# max-age: 1800
|
||||||
|
mail:
|
||||||
|
from: sasiedzi@localhost
|
||||||
|
api-docs:
|
||||||
|
default-include-pattern: /api/**
|
||||||
|
management-include-pattern: /management/**
|
||||||
|
title: Sasiedzi API
|
||||||
|
description: Sasiedzi API documentation
|
||||||
|
version: 0.0.1
|
||||||
|
terms-of-service-url:
|
||||||
|
contact-name:
|
||||||
|
contact-url:
|
||||||
|
contact-email:
|
||||||
|
license: unlicensed
|
||||||
|
license-url:
|
||||||
|
security:
|
||||||
|
content-security-policy: "default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:"
|
||||||
|
oauth2:
|
||||||
|
audience:
|
||||||
|
- account
|
||||||
|
- api://default
|
||||||
|
# ===================================================================
|
||||||
|
# Application specific properties
|
||||||
|
# Add your own application properties here, see the ApplicationProperties class
|
||||||
|
# to have type-safe configuration, like in the JHipsterProperties above
|
||||||
|
#
|
||||||
|
# More documentation is available at:
|
||||||
|
# https://www.jhipster.tech/common-application-properties/
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# application:
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user