101 Commits

Author SHA1 Message Date
97e5d6723f Update dependency org.springframework.boot:spring-boot-starter-json to v3.5.10
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2026-01-22 13:01:55 +00:00
2831f03ac1 Merge pull request 'Update dependency org.springframework.boot:spring-boot-starter-data-jpa to v3.5.7' (!12) from renovate/org.springframework.boot-spring-boot-starter-data-jpa-3.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #12
2025-11-06 19:06:11 +01:00
e5ffea3196 Merge pull request 'Update dependency org.springframework.boot:spring-boot-starter-json to v3.5.7' (!13) from renovate/org.springframework.boot-spring-boot-starter-json-3.x into main
Some checks failed
continuous-integration/drone/push Build was killed
Reviewed-on: #13
2025-11-06 19:06:03 +01:00
d0776d7f0b Merge pull request 'Update dependency org.springframework.boot:spring-boot-starter-security to v3.5.7' (!14) from renovate/org.springframework.boot-spring-boot-starter-security-3.x into main
Some checks failed
continuous-integration/drone/push Build was killed
Reviewed-on: #14
2025-11-06 19:05:56 +01:00
2acafb87eb Merge pull request 'Update dependency org.springframework.boot:spring-boot-starter-web to v3.5.7' (!15) from renovate/org.springframework.boot-spring-boot-starter-web-3.x into main
Some checks failed
continuous-integration/drone/push Build was killed
Reviewed-on: #15
2025-11-06 19:05:49 +01:00
f733b03213 Merge pull request 'Update dependency org.apache.logging.log4j:log4j-api to v2.25.2' (!19) from renovate/org.apache.logging.log4j-log4j-api-2.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #19
2025-11-06 19:04:39 +01:00
2c3246660e Merge pull request 'Update dependency org.owasp:dependency-check-maven to v12.1.8' (!17) from renovate/org.owasp-dependency-check-maven-12.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #17
2025-11-06 19:04:30 +01:00
d98991c0a0 Merge pull request 'Update dependency org.apache.logging.log4j:log4j-core to v2.25.2' (!20) from renovate/org.apache.logging.log4j-log4j-core-2.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #20
2025-11-06 19:04:22 +01:00
d0f0e72eb2 Merge pull request 'Update dependency io.jsonwebtoken:jjwt-api to v0.13.0' (!21) from renovate/io.jsonwebtoken-jjwt-api-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #21
2025-11-06 19:04:10 +01:00
ad675f06f0 Merge pull request 'Update dependency io.jsonwebtoken:jjwt-impl to v0.13.0' (!22) from renovate/io.jsonwebtoken-jjwt-impl-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #22
2025-11-06 19:04:00 +01:00
90334d15f8 Merge pull request 'Update dependency io.jsonwebtoken:jjwt-jackson to v0.13.0' (!23) from renovate/io.jsonwebtoken-jjwt-jackson-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #23
2025-11-06 19:03:52 +01:00
02be0a405e Update dependency io.jsonwebtoken:jjwt-jackson to v0.13.0
Some checks failed
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build is failing
2025-11-06 18:02:14 +00:00
574dd4c093 Merge pull request 'Update dependency org.postgresql:postgresql to v42.7.8' (!18) from renovate/org.postgresql-postgresql-42.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #18
2025-11-06 18:56:16 +01:00
e69462be26 Update dependency org.springframework.boot:spring-boot-starter-web to v3.5.7 2025-11-06 13:45:20 +00:00
2024c356b0 Update dependency org.springframework.boot:spring-boot-starter-security to v3.5.7 2025-11-06 13:45:18 +00:00
66233827fd Update dependency org.springframework.boot:spring-boot-starter-json to v3.5.7 2025-11-06 13:45:16 +00:00
02bd377430 Update dependency org.springframework.boot:spring-boot-starter-data-jpa to v3.5.7 2025-11-06 13:45:14 +00:00
a2e69c6a57 Update dependency org.apache.logging.log4j:log4j-core to v2.25.2 2025-11-06 13:45:12 +00:00
bbbf34da6e Update dependency org.apache.logging.log4j:log4j-api to v2.25.2 2025-11-06 13:45:10 +00:00
e05e523c12 Update dependency io.jsonwebtoken:jjwt-impl to v0.13.0 2025-11-06 13:45:09 +00:00
8d6bb14fc1 Update dependency io.jsonwebtoken:jjwt-api to v0.13.0 2025-11-06 13:45:07 +00:00
91415bea5b Update dependency org.postgresql:postgresql to v42.7.8 2025-11-06 13:45:05 +00:00
18ca42a056 Update dependency org.owasp:dependency-check-maven to v12.1.8 2025-11-06 13:45:03 +00:00
8029f2d931 Merge pull request 'Update dependency org.postgresql:postgresql to v42.7.6' (!16) from renovate/org.postgresql-postgresql-42.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #16
2025-06-01 20:27:05 +02:00
d6ffe35d6e Update dependency org.postgresql:postgresql to v42.7.6
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-05-28 11:00:57 +00:00
8b5ba88d24 Merge pull request 'Update dependency org.owasp:dependency-check-maven to v12.1.1' (!11) from renovate/org.owasp-dependency-check-maven-12.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #11
2025-04-13 00:55:35 +02:00
86742f1f1a Update dependency org.owasp:dependency-check-maven to v12.1.1
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2025-04-05 13:01:12 +00:00
8acc7460a0 Merge pull request 'Update dependency org.apache.logging.log4j:log4j-api to v2.24.3' (!9) from renovate/org.apache.logging.log4j-log4j-api-2.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #9
2025-03-23 15:29:10 +01:00
656a337501 Merge pull request 'Update dependency org.apache.logging.log4j:log4j-core to v2.24.3' (!10) from renovate/org.apache.logging.log4j-log4j-core-2.x into main
Some checks failed
continuous-integration/drone/push Build was killed
Reviewed-on: #10
2025-03-23 15:28:56 +01:00
8eaae7c659 Update dependency org.apache.logging.log4j:log4j-core to v2.24.3
Some checks failed
continuous-integration/drone/push Build was killed
continuous-integration/drone/pr Build was killed
2025-03-23 14:26:51 +00:00
6c077bce27 Update dependency org.apache.logging.log4j:log4j-api to v2.24.3
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build was killed
2025-03-23 14:26:48 +00:00
7a0ee4b438 Merge pull request 'Update dependency org.springframework.boot:spring-boot-starter-json to v3.4.4' (!5) from renovate/org.springframework.boot-spring-boot-starter-json-3.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #5
2025-03-23 15:17:21 +01:00
03ea142db2 Merge pull request 'Update dependency org.springframework.boot:spring-boot-starter-security to v3.4.4' (!6) from renovate/org.springframework.boot-spring-boot-starter-security-3.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #6
2025-03-23 15:17:12 +01:00
d285783fe7 fix build with new jsonwebtoken api
Some checks failed
continuous-integration/drone/push Build was killed
2025-03-23 15:16:19 +01:00
eced10976b Merge pull request 'Update dependency org.springframework.boot:spring-boot-starter-web to v3.4.4' (!7) from renovate/org.springframework.boot-spring-boot-starter-web-3.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #7
2025-03-23 14:56:32 +01:00
eadeb8c518 Merge pull request 'Update dependency io.jsonwebtoken:jjwt-api to v0.12.6' (!8) from renovate/io.jsonwebtoken-jjwt-api-0.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #8
2025-03-23 14:56:19 +01:00
6ffa22d7b5 Update dependency io.jsonwebtoken:jjwt-api to v0.12.6
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-03-23 13:28:05 +00:00
dacdfd7935 Update dependency org.springframework.boot:spring-boot-starter-web to v3.4.4
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-03-23 13:28:03 +00:00
a728e376f2 Update dependency org.springframework.boot:spring-boot-starter-security to v3.4.4
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2025-03-23 12:27:16 +00:00
9890086810 Update dependency org.springframework.boot:spring-boot-starter-json to v3.4.4
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-03-23 12:27:14 +00:00
2c3c2b783e Merge pull request 'Update dependency org.postgresql:postgresql to v42.7.5' (!3) from renovate/org.postgresql-postgresql-42.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #3
2025-03-23 12:54:33 +01:00
c5c76c77fa Merge pull request 'Update dependency org.springframework.boot:spring-boot-starter-data-jpa to v3.4.4' (!4) from renovate/org.springframework.boot-spring-boot-starter-data-jpa-3.x into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #4
2025-03-23 12:54:16 +01:00
992e57a46b Update dependency org.springframework.boot:spring-boot-starter-data-jpa to v3.4.4
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-03-23 11:29:41 +00:00
638bdd9b7b Update dependency org.postgresql:postgresql to v42.7.5
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-03-23 11:29:37 +00:00
d35063519e Merge pull request 'Configure Renovate' (!2) from renovate/configure into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #2
2025-03-23 11:58:57 +01:00
eb81f6b26a Add renovate.json
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2025-03-22 23:13:03 +00:00
58d59f17e0 fix sonar warning
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2025-03-09 13:53:35 +01:00
cce4d4a25d update pipe name
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-09 12:07:48 +01:00
b23b6f0dbd add repository info
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2025-03-09 11:55:33 +01:00
62bf6229a7 add suppressions file
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is failing
2025-03-09 11:33:20 +01:00
cf297ffe94 update vulnerable deps
Some checks failed
continuous-integration/drone/push Build is failing
2025-03-09 11:26:07 +01:00
d27b2054eb add api key
Some checks failed
continuous-integration/drone/push Build is failing
2025-03-09 11:20:19 +01:00
178b77e868 fix dependency-check
Some checks failed
continuous-integration/drone/push Build was killed
2025-03-09 11:04:44 +01:00
23d46a28fb implement dependency-check step
Some checks failed
continuous-integration/drone/push Build is failing
2025-03-09 10:58:55 +01:00
dd156d3152 fix deploy pipe
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-09 10:57:30 +01:00
cf1c3eefc8 implement deploy pipe
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is failing
2025-03-09 10:54:53 +01:00
920bddc0db Fix DroneCI
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-08 14:24:18 +01:00
4887902516 Update DroneCI pipeline Java version
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-08 14:22:46 +01:00
9012e61e61 Update Java version requirements
Some checks failed
continuous-integration/drone/push Build is failing
2024-11-11 16:39:54 +01:00
0f9816461f Fix duplication
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-11 16:35:10 +01:00
ea3860391d Update version
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-11 16:33:24 +01:00
17ba5b8b9d RLH-21 - Fix Sonar
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-11 16:29:59 +01:00
9b3f43937f RLH-21 - Fix Sonar
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-11 16:25:48 +01:00
7ffb02bbf0 RLH-21 - Fix Sonar
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-11 16:24:28 +01:00
c51486751f RLH-21 - Fix Sonar
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-11 16:23:06 +01:00
211aa26df1 RLH-19 - Use UserDto in init query
All checks were successful
continuous-integration/drone Build is passing
2024-11-11 16:11:47 +01:00
5a706e56ae RLH-19 - Implement UserDto 2024-11-11 16:11:37 +01:00
5175cb89b5 RLH-17 - Update Account APIs and implement rename 2024-11-04 11:22:34 +01:00
a87c692a25 RLH-4 - Implement /users/delete API 2024-11-03 17:58:00 +01:00
c5315daf41 RLH-3 - Implement account deletion logic 2024-11-03 14:43:44 +01:00
2f6d4a9f1b RLH-5 - integrate YouTrack 2024-11-03 12:54:57 +01:00
cdcc899453 RLH-5 - integrate YouTrack 2024-11-03 12:53:55 +01:00
2cad1c76cc RLH-5 - integrate YouTrack 2024-11-03 12:31:51 +01:00
d656ab54eb add checks for login process
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-09 06:00:36 +02:00
86f4b05b8c optimize imports
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-09 05:27:34 +02:00
17dcc0ac4f implement registration validity checks
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-09 04:56:54 +02:00
c0f655b0df rename main class
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-08 12:01:10 +02:00
9eea30b23e merge user services
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 23:03:31 +02:00
4c79d502e3 rename packages and classes
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 22:57:28 +02:00
3443dcaa95 use constant for property
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 22:53:37 +02:00
5bd9ff42ec implement json exception handling
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 22:51:47 +02:00
4990b5b92c implement users resource and email based auth
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 22:37:00 +02:00
fea42d33eb improve variable names
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 21:30:30 +02:00
72e7af7a35 improve code quality
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 21:29:10 +02:00
a0fafcc2dc RHSRV-4 | RHSRV-5 - Implement Authentication with JWT
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 21:14:19 +02:00
9fa1a4bfba update readme
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 13:35:51 +02:00
0d9932df45 add wiki to support section
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 11:30:40 +02:00
5c2a2672a8 fix project name
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 10:34:08 +02:00
09455ac694 add section about running in readme.md
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 10:33:41 +02:00
f361fcf5ff add readme.md
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 10:30:35 +02:00
b88a9f75bf optimize code
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 10:21:35 +02:00
f142912471 implement API Json error and generator
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 10:10:10 +02:00
fe03484427 improve code quality
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 01:05:02 +02:00
a0dfb3fa59 use spring boot autowired for service injection
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 00:57:39 +02:00
c7003c1d02 refactor code structure
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 00:46:27 +02:00
36c9f4f188 implement JSON API handling
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 00:38:48 +02:00
223babece8 optimize code
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 00:02:43 +02:00
33f09270e5 Fix variable name error
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-07 00:01:52 +02:00
88a9b2b706 RHSRV-3 - Implement Hibernate PSQL with basic /register API
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-06 23:57:50 +02:00
Bea
f85a3982a4 Merge pull request 'RHSRV-1 - Choose a web framework' (#1) from spring-boot into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #1
Issue link: https://plane.beatrice.wtf/release-hive/projects/925069c2-6e79-4180-8960-e22aa56e19bd/issues/1ac76823-6bd4-4b3c-85e8-1954073cc4a7
2024-08-06 21:53:25 +02:00
61c0216a3e RHSRV-1 - implement basic spring boot
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2024-08-06 21:48:15 +02:00
33 changed files with 1335 additions and 24 deletions

View File

@@ -1,27 +1,41 @@
kind: pipeline
name: default
name: verify
platform:
os: linux
arch: arm64
trigger:
event:
- push
- pull_request
steps:
# test if it compiles correctly
- name: build
image: maven:3-eclipse-temurin-17
image: maven:3-eclipse-temurin-21
commands:
- mvn verify --no-transfer-progress -DskipTests=true -Dmaven.javadoc.skip=true -B -V
# run unit tests
- name: test
image: maven:3-eclipse-temurin-17
image: maven:3-eclipse-temurin-21
commands:
- mvn test --no-transfer-progress -B -V
# check maven dependencies
- name: dependency-check
image: maven:3-eclipse-temurin-21
commands:
- mvn dependency-check:check --no-transfer-progress -B -V -DnvdApiKey=$NVD_API_KEY
environment:
NVD_API_KEY:
from_secret: nvd_api_key
# run code analysis
- name: code-analysis
image: maven:3-eclipse-temurin-17
image: maven:3-eclipse-temurin-21
commands:
- mvn sonar:sonar --no-transfer-progress -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.host.url=$SONAR_INSTANCE_URL -Dsonar.login=$SONAR_LOGIN_KEY -B -V
- mvn sonar:sonar --no-transfer-progress -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.host.url=$SONAR_INSTANCE_URL -Dsonar.token=$SONAR_LOGIN_KEY -B -V
environment:
SONAR_PROJECT_KEY:
from_secret: sonar_project_key
@@ -29,3 +43,32 @@ steps:
from_secret: sonar_instance_url
SONAR_LOGIN_KEY:
from_secret: sonar_login_key
---
kind: pipeline
name: deploy
type: docker
platform:
os: linux
arch: arm64
trigger:
event:
- promote
target:
- production
steps:
# skip all previous steps because they were already ran in the "build" phase; we don't need to re-analyze the code.
# upload to maven repository
- name: maven-deploy
image: maven:3-eclipse-temurin-21
commands:
- mvn deploy --no-transfer-progress -DskipTests=true -Dmaven.javadoc.skip=true -B -V -gs settings.xml -Dmaven.repo.username=$MAVEN_REPO_USERNAME -Dmaven.repo.password=$MAVEN_REPO_PASSWORD
environment:
MAVEN_REPO_USERNAME:
from_secret: maven_repo_username
MAVEN_REPO_PASSWORD:
from_secret: maven_repo_password

92
pom.xml
View File

@@ -6,25 +6,107 @@
<groupId>wtf.beatrice</groupId>
<artifactId>release-hive</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>0.0.2-SNAPSHOT</version>
<dependencies>
<!-- Logging Dependencies -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.23.1</version>
<version>2.25.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
<version>2.25.2</version>
</dependency>
<!-- Web Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.5.7</version>
</dependency>
<!-- Security and Auth -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.13.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.13.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.13.0</version>
</dependency>
<!-- Database Dependencies -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>12.1.8</version>
<configuration>
<failBuildOnCVSS>8</failBuildOnCVSS>
<nvdApiKey>${nvdApiKey}</nvdApiKey>
<formats>
<format>html</format>
<format>json</format>
</formats>
<suppressionFiles>
<suppressionFile>./suppressions.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<sonar.dependencyCheck.htmlReportPath>./target/dependency-check-report.html</sonar.dependencyCheck.htmlReportPath>
<sonar.dependencyCheck.jsonReportPath>./target/dependency-check-report.json</sonar.dependencyCheck.jsonReportPath>
<sonar.dependencyCheck.summarize>true</sonar.dependencyCheck.summarize>
</properties>
<distributionManagement>
<repository>
<id>nexus-releases</id>
<url>https://nexus.beatrice.wtf/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<url>https://nexus.beatrice.wtf/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>

33
readme.md Normal file
View File

@@ -0,0 +1,33 @@
# release-hive back end
[![Build Status](https://drone.beatrice.wtf/api/badges/bea/release-hive/status.svg)](https://drone.beatrice.wtf/bea/release-hive)
[![Quality Gate Status](https://sonar.beatrice.wtf/api/project_badges/measure?project=relhive&metric=alert_status&token=sqb_4f7b91f9a42424c2435f3d14de1fe4654a3cab5d)](https://sonar.beatrice.wtf/dashboard?id=relhive)
[![Reliability Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=relhive&metric=reliability_rating&token=sqb_4f7b91f9a42424c2435f3d14de1fe4654a3cab5d)](https://sonar.beatrice.wtf/dashboard?id=relhive)
[![Security Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=relhive&metric=security_rating&token=sqb_4f7b91f9a42424c2435f3d14de1fe4654a3cab5d)](https://sonar.beatrice.wtf/dashboard?id=relhive)
[![Maintainability Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=relhive&metric=sqale_rating&token=sqb_4f7b91f9a42424c2435f3d14de1fe4654a3cab5d)](https://sonar.beatrice.wtf/dashboard?id=relhive)
*pure java server backend for software release management*
## running
### database requirements
set up a database server with postgresql.
this is currently the only supported database backend.
## building
### required tools
- java 21+ sdk
- git
- maven
### build steps
the build process currently happens with maven.
specific build and release steps have not been specified yet.
## support
| category | info |
|---------------------|-------------------------------------------------------------------|
| official wiki | [release-hive wiki](https://wiki.beatrice.wtf/en/release-hive) |
| official repository | [release-hive src](https://git.beatrice.wtf/bea/release-hive.git) |
| build status | [drone-ci](https://drone.beatrice.wtf/bea/release-hive) |
| dev email | [hello@beatrice.wtf](mailto:hello@beatrice.wtf) |

3
renovate.json Normal file
View File

@@ -0,0 +1,3 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

20
settings.xml Normal file
View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">
<servers>
<server>
<id>nexus-snapshots</id>
<username>${maven.repo.username}</username>
<password>${maven.repo.password}</password>
</server>
<server>
<id>nexus-releases</id>
<username>${maven.repo.username}</username>
<password>${maven.repo.password}</password>
</server>
</servers>
</settings>

View File

@@ -0,0 +1,49 @@
package wtf.beatrice.releasehive;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import wtf.beatrice.releasehive.db.HibernateManager;
import wtf.beatrice.releasehive.models.User;
import java.util.List;
@SpringBootApplication
public class HiveMain {
private static final Logger LOGGER = LogManager.getLogger(HiveMain.class);
public static void main(String[] args) {
LOGGER.info("Registering shutdown hooks");
Runtime.getRuntime().addShutdownHook(shutdownHook);
LOGGER.info("Initializing database backend");
HibernateManager.initialize();
LOGGER.info("Initializing Spring Boot");
SpringApplication.run(HiveMain.class, args);
LOGGER.info("Spring Boot & DB initialized!");
Session session = HibernateManager.getSession();
Transaction transaction = session.beginTransaction();
List<User> users = session.createQuery("FROM User", User.class).getResultList();
transaction.commit();
StringBuilder usersListBuilder = new StringBuilder("[");
users.forEach(user -> usersListBuilder.append(user.getUsername()).append(","));
usersListBuilder.deleteCharAt(usersListBuilder.length() - 1);
usersListBuilder.append("]");
LOGGER.info("Found users: {}", usersListBuilder);
LOGGER.info("Total: {}", users.size());
}
private static final Thread shutdownHook = new Thread(() -> {
HibernateManager.shutdown();
LOGGER.info("Shutting down!");
});
}

View File

@@ -1,14 +0,0 @@
package wtf.beatrice.releasehive;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Main {
private static final Logger LOGGER = LogManager.getLogger(Main.class);
public static void main(String[] args) {
LOGGER.info("Hello world!");
}
}

View File

@@ -0,0 +1,51 @@
package wtf.beatrice.releasehive.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import wtf.beatrice.releasehive.repositories.UserRepository;
@Configuration
public class ApplicationConfiguration
{
private final UserRepository userRepository;
public ApplicationConfiguration(@Autowired UserRepository userRepository) {
this.userRepository = userRepository;
}
@Bean
UserDetailsService userDetailsService() {
return authParameter -> userRepository.findByEmail(authParameter)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
@Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
}

View File

@@ -0,0 +1,13 @@
package wtf.beatrice.releasehive.config;
public class InternalConfiguration
{
private InternalConfiguration() {
throw new IllegalStateException("Utility class");
}
public static final String EMAIL_REGEX_RCF = "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])";
public static final String USERNAME_REGEX = "^[a-zA-Z0-9_-]{3,16}$";
public static final String PASSWORD_REGEX = "^.{6,128}$";
}

View File

@@ -0,0 +1,79 @@
package wtf.beatrice.releasehive.config;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import wtf.beatrice.releasehive.models.User;
import wtf.beatrice.releasehive.services.JWTService;
import wtf.beatrice.releasehive.services.UserService;
import java.io.IOException;
@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter
{
private final HandlerExceptionResolver handlerExceptionResolver;
private final JWTService jwtService;
private final UserService userService;
public JWTAuthenticationFilter(
@Autowired JWTService jwtService,
@Autowired UserService userService,
@Autowired HandlerExceptionResolver handlerExceptionResolver) {
this.jwtService = jwtService;
this.userService = userService;
this.handlerExceptionResolver = handlerExceptionResolver;
}
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
try {
final String jwt = authHeader.substring(7);
final String email = jwtService.extractEmail(jwt);
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (email != null && authentication == null) {
User userDetails = userService.loadUserByEmail(email);
if (jwtService.isTokenValid(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
} catch (Exception exception) {
handlerExceptionResolver.resolveException(request, response, null, exception);
}
}
}

View File

@@ -0,0 +1,72 @@
package wtf.beatrice.releasehive.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration
{
private final AuthenticationProvider authenticationProvider;
private final JWTAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfiguration(
@Autowired JWTAuthenticationFilter jwtAuthenticationFilter,
@Autowired AuthenticationProvider authenticationProvider)
{
this.authenticationProvider = authenticationProvider;
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(registry -> registry
.requestMatchers("/api/v1/auth/**")
.permitAll()
.anyRequest()
.authenticated());
http.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:8080"));
configuration.setAllowedMethods(List.of("GET","POST"));
configuration.setAllowedHeaders(List.of("Authorization","Content-Type"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",configuration);
return source;
}
}

View File

@@ -0,0 +1,43 @@
package wtf.beatrice.releasehive.db;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class HibernateManager
{
private static Session session;
private static SessionFactory sessionFactory;
private HibernateManager() {
}
public static void initialize() {
sessionFactory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
}
public static void shutdown() {
if(session != null && !session.isOpen()) {
session.close();
}
if(sessionFactory != null && sessionFactory.isOpen()) {
sessionFactory.close();
}
}
public static Session getSession() {
if(session != null && (!session.isOpen() || !session.isConnected())) {
session.close();
}
session = sessionFactory.openSession();
return session;
}
public static Transaction beginTransaction() {
return getSession().beginTransaction();
}
}

View File

@@ -0,0 +1,25 @@
package wtf.beatrice.releasehive.dtos;
import java.util.UUID;
public class EditUsernameAccountDto
{
private String username;
private UUID uuid;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
}

View File

@@ -0,0 +1,24 @@
package wtf.beatrice.releasehive.dtos;
public class LoginUserDto
{
private String email;
private String password;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@@ -0,0 +1,34 @@
package wtf.beatrice.releasehive.dtos;
public class RegisterUserDto
{
private String email;
private String password;
private String username;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

View File

@@ -0,0 +1,9 @@
package wtf.beatrice.releasehive.exceptions;
public class HiveException extends Exception
{
public HiveException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,73 @@
package wtf.beatrice.releasehive.exceptions;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.coyote.BadRequestException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ProblemDetail;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.nio.file.AccessDeniedException;
import java.security.SignatureException;
@RestControllerAdvice
public class RestExceptionHandler
{
private static final Logger LOGGER = LogManager.getLogger(RestExceptionHandler.class);
private static final String DESCRIPTION_PROPERTY = "description";
@ExceptionHandler(Exception.class)
public ProblemDetail handleSecurityException(Exception exception) {
ProblemDetail errorDetail = null;
LOGGER.error(exception);
if(exception instanceof IllegalArgumentException) {
errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(400), exception.getMessage());
errorDetail.setProperty(DESCRIPTION_PROPERTY, "Invalid argument cannot be parsed");
}
if(exception instanceof BadRequestException) {
errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(400), exception.getMessage());
errorDetail.setProperty(DESCRIPTION_PROPERTY, "Bad request");
}
if (exception instanceof BadCredentialsException) {
errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(401), exception.getMessage());
errorDetail.setProperty(DESCRIPTION_PROPERTY, "Invalid email or password");
return errorDetail;
}
if (exception instanceof AccountStatusException) {
errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(403), exception.getMessage());
errorDetail.setProperty(DESCRIPTION_PROPERTY, "Account locked");
}
if (exception instanceof AccessDeniedException) {
errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(403), exception.getMessage());
errorDetail.setProperty(DESCRIPTION_PROPERTY, "You are not authorized to access this resource");
}
if (exception instanceof SignatureException) {
errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(403), exception.getMessage());
errorDetail.setProperty(DESCRIPTION_PROPERTY, "Invalid JWT signature");
}
if (exception instanceof ExpiredJwtException) {
errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(403), exception.getMessage());
errorDetail.setProperty(DESCRIPTION_PROPERTY, "Expired JWT token");
}
if (errorDetail == null) {
errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(500), exception.getMessage());
errorDetail.setProperty(DESCRIPTION_PROPERTY, "Internal server error");
}
return errorDetail;
}
}

View File

@@ -0,0 +1,34 @@
package wtf.beatrice.releasehive.models;
public class LoginResponse
{
private String token;
private long expiresIn;
public String getToken() {
return token;
}
public LoginResponse setToken(String token) {
this.token = token;
return this;
}
public long getExpiresIn() {
return expiresIn;
}
public LoginResponse setExpiresIn(long expiresIn) {
this.expiresIn = expiresIn;
return this;
}
@Override
public String toString() {
return "LoginResponse{" +
"token='" + token + '\'' +
", expiresIn=" + expiresIn +
'}';
}
}

View File

@@ -0,0 +1,114 @@
package wtf.beatrice.releasehive.models;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Entity
@Table(name="users")
public class User implements UserDetails
{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID uuid;
@Column(nullable = false)
private String username;
@Column(unique = true, length = 64, nullable = false)
private String email;
@Column(nullable = false)
private String password;
@CreationTimestamp
@Column(updatable = false, name = "created_at")
private Date createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private Date updatedAt;
public UUID getUuid() {
return uuid;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@@ -0,0 +1,16 @@
package wtf.beatrice.releasehive.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import wtf.beatrice.releasehive.models.User;
import java.util.Optional;
import java.util.UUID;
@Repository
public interface UserRepository extends JpaRepository<User, UUID> {
Optional<User> findByEmail(String email);
Optional<User> findByUsername(String username);
}

View File

@@ -0,0 +1,44 @@
package wtf.beatrice.releasehive.resources;
import org.apache.coyote.BadRequestException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import wtf.beatrice.releasehive.dtos.EditUsernameAccountDto;
import wtf.beatrice.releasehive.services.AccountService;
import wtf.beatrice.releasehive.services.UserService;
import java.util.UUID;
@RestController
@RequestMapping("/api/v1/account")
public class AccountResource {
private final AccountService accountService;
private final UserService userService;
public AccountResource(
@Autowired AccountService accountService,
@Autowired UserService userService) {
this.accountService = accountService;
this.userService = userService;
}
@PostMapping(
value="/edit",
produces= MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> register(@RequestBody EditUsernameAccountDto userDto) throws BadRequestException {
String username = accountService.changeUsername(userDto);
return ResponseEntity.ok(username);
}
@DeleteMapping(
value = "/delete/{id}",
produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<Boolean> deleteUser(@PathVariable("id") String id) {
UUID uuid = UUID.fromString(id);
boolean deleted = userService.deleteUser(uuid);
return ResponseEntity.ok(deleted);
}
}

View File

@@ -0,0 +1,53 @@
package wtf.beatrice.releasehive.resources;
import org.apache.coyote.BadRequestException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import wtf.beatrice.releasehive.dtos.LoginUserDto;
import wtf.beatrice.releasehive.dtos.RegisterUserDto;
import wtf.beatrice.releasehive.models.LoginResponse;
import wtf.beatrice.releasehive.models.User;
import wtf.beatrice.releasehive.services.AccountService;
import wtf.beatrice.releasehive.services.JWTService;
@RestController
@RequestMapping("/api/v1/auth")
public class AuthResource {
private final AccountService accountService;
private final JWTService jwtService;
public AuthResource(
@Autowired AccountService accountService,
@Autowired JWTService jwtService) {
this.accountService = accountService;
this.jwtService = jwtService;
}
@PostMapping(
value="/register",
produces= MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> register(@RequestBody RegisterUserDto userDto) throws BadRequestException {
User user = accountService.register(userDto);
return ResponseEntity.ok(user);
}
@PostMapping(
value="/login",
produces= MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<LoginResponse> login(@RequestBody LoginUserDto userDto) throws BadRequestException {
User authenticatedUser = accountService.authenticate(userDto);
String jwtToken = jwtService.generateToken(authenticatedUser);
LoginResponse loginResponse = new LoginResponse().setToken(jwtToken).setExpiresIn(jwtService.getExpirationTime());
return ResponseEntity.ok(loginResponse);
}
}

View File

@@ -0,0 +1,44 @@
package wtf.beatrice.releasehive.resources;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import wtf.beatrice.releasehive.models.User;
import wtf.beatrice.releasehive.services.UserService;
import java.util.List;
@RestController
@RequestMapping("/api/v1/users")
public class UserResource
{
private final UserService userService;
public UserResource(@Autowired UserService userService)
{
this.userService = userService;
}
@GetMapping(
value = "/me",
produces= MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> authenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User currentUser = (User) authentication.getPrincipal();
return ResponseEntity.ok(currentUser);
}
@GetMapping(
value = "/all",
produces= MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}
}

View File

@@ -0,0 +1,17 @@
package wtf.beatrice.releasehive.services;
import org.apache.coyote.BadRequestException;
import wtf.beatrice.releasehive.dtos.EditUsernameAccountDto;
import wtf.beatrice.releasehive.dtos.LoginUserDto;
import wtf.beatrice.releasehive.dtos.RegisterUserDto;
import wtf.beatrice.releasehive.models.User;
public interface AccountService
{
User register(RegisterUserDto user) throws BadRequestException;
User authenticate(LoginUserDto user) throws BadRequestException;
String changeUsername(EditUsernameAccountDto editData) throws BadRequestException;
}

View File

@@ -0,0 +1,118 @@
package wtf.beatrice.releasehive.services;
import org.apache.coyote.BadRequestException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import wtf.beatrice.releasehive.config.InternalConfiguration;
import wtf.beatrice.releasehive.dtos.EditUsernameAccountDto;
import wtf.beatrice.releasehive.dtos.LoginUserDto;
import wtf.beatrice.releasehive.dtos.RegisterUserDto;
import wtf.beatrice.releasehive.models.User;
import wtf.beatrice.releasehive.repositories.UserRepository;
@Service
public class AccountServiceImpl implements AccountService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
public AccountServiceImpl(
@Autowired UserRepository userRepository,
@Autowired AuthenticationManager authenticationManager,
@Autowired PasswordEncoder passwordEncoder)
{
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public User register(RegisterUserDto userDto) throws BadRequestException {
if (userDto.getEmail() == null ||
userDto.getEmail().isEmpty() ||
userDto.getPassword() == null ||
userDto.getPassword().isEmpty() ||
userDto.getUsername() == null ||
userDto.getUsername().isEmpty()) {
throw new BadRequestException("Please provide a valid email, password, and username");
}
if(!userDto.getEmail().matches(InternalConfiguration.EMAIL_REGEX_RCF)) {
throw new BadRequestException("Invalid email format");
}
if(!userDto.getUsername().matches(InternalConfiguration.USERNAME_REGEX)) {
throw new BadRequestException("Username contains invalid characters");
}
if(!userDto.getPassword().matches(InternalConfiguration.PASSWORD_REGEX)) {
throw new BadRequestException("Invalid password format");
}
if (userRepository.findByEmail(userDto.getEmail()).isPresent()) {
throw new BadRequestException("An account already exists with this email");
}
if(userRepository.findByUsername(userDto.getUsername()).isPresent()) {
throw new BadRequestException("Username already in use");
}
User user = new User();
user.setUsername(userDto.getUsername());
user.setEmail(userDto.getEmail());
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
return userRepository.save(user);
}
@Override
public User authenticate(LoginUserDto userDto) throws BadRequestException {
if (userDto.getEmail() == null ||
userDto.getEmail().isEmpty() ||
userDto.getPassword() == null ||
userDto.getPassword().isEmpty()) {
throw new BadRequestException("Please provide a valid email and password");
}
if(!userDto.getEmail().matches(InternalConfiguration.EMAIL_REGEX_RCF)) {
throw new BadRequestException("Invalid email format");
}
if(!userDto.getPassword().matches(InternalConfiguration.PASSWORD_REGEX)) {
throw new BadRequestException("Invalid password format");
}
if (!userRepository.findByEmail(userDto.getEmail()).isPresent()) {
throw new BadRequestException("No account registered with this email");
}
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
userDto.getEmail(),
userDto.getPassword()));
return userRepository.findByEmail(userDto.getEmail())
.orElseThrow();
}
@Override
public String changeUsername(EditUsernameAccountDto editData) {
User user = userRepository
.findById(editData.getUuid())
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
user.setUsername(editData.getUsername());
userRepository.save(user);
return user.getUsername();
}
}

View File

@@ -0,0 +1,88 @@
package wtf.beatrice.releasehive.services;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import wtf.beatrice.releasehive.models.User;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Service
public class JWTService
{
@Value("${security.jwt.secret-key}")
private String secretKey;
@Value("${security.jwt.expiration-time}")
private long jwtExpiration;
public String extractEmail(String token) {
return extractClaim(token, Claims::getSubject);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
public String generateToken(User userDetails) {
return generateToken(new HashMap<>(), userDetails);
}
public String generateToken(Map<String, Object> extraClaims, User userDetails) {
return buildToken(extraClaims, userDetails, jwtExpiration);
}
public long getExpirationTime() {
return jwtExpiration;
}
private String buildToken(
Map<String, Object> extraClaims,
User userDetails,
long expiration
) {
return Jwts
.builder()
.claims(extraClaims)
.subject(userDetails.getEmail())
.issuedAt(new Date(System.currentTimeMillis()))
.expiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSignInKey(), Jwts.SIG.HS256)
.compact();
}
public boolean isTokenValid(String token, User userDetails) {
final String email = extractEmail(token);
return (email.equals(userDetails.getEmail())) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
private Claims extractAllClaims(String token) {
return Jwts
.parser()
.verifyWith(getSignInKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
private SecretKey getSignInKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}
}

View File

@@ -0,0 +1,18 @@
package wtf.beatrice.releasehive.services;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import wtf.beatrice.releasehive.models.User;
import java.util.List;
import java.util.UUID;
public interface UserService
{
List<User> getAllUsers();
User loadUserByUsername(String username) throws UsernameNotFoundException;
User loadUserByEmail(String email) throws UsernameNotFoundException;
boolean deleteUser(UUID id);
}

View File

@@ -0,0 +1,44 @@
package wtf.beatrice.releasehive.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import wtf.beatrice.releasehive.models.User;
import wtf.beatrice.releasehive.repositories.UserRepository;
import java.util.List;
import java.util.UUID;
@Service
public class UserServiceImpl implements UserService
{
private final UserRepository userRepository;
public UserServiceImpl(@Autowired UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public List<User> getAllUsers() {
return userRepository.findAll();
}
@Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException(username));
}
@Override
public User loadUserByEmail(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException(email));
}
@Override
public boolean deleteUser(UUID id) {
userRepository.delete(userRepository
.findById(id)
.orElseThrow(() -> new UsernameNotFoundException("User not found")));
return true;
}
}

View File

@@ -0,0 +1,26 @@
package wtf.beatrice.releasehive.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JsonUtil
{
private JsonUtil() {
throw new AssertionError("Utility class");
}
private static final Logger LOGGER = LogManager.getLogger(JsonUtil.class);
private static final ObjectMapper MAPPER = new ObjectMapper();
public static String convertToJson(Object input) {
try {
return MAPPER.writeValueAsString(input);
} catch (JsonProcessingException e) {
LOGGER.error(e);
return e.getMessage();
}
}
}

View File

@@ -0,0 +1,13 @@
server.port=8080
spring.datasource.url=jdbc:postgresql://localhost:5432/releasehive
spring.datasource.username=relhive
spring.datasource.password=beelover
## Hibernate properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=false
security.jwt.secret-key=ed725256582a23e94f81ba36d7df498ea330c7ba978e2d8876fe135b4bb34068
# 1h in millisecond
security.jwt.expiration-time=3600000

View File

@@ -0,0 +1,5 @@
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
- org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Connection settings -->
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.url">jdbc:postgresql://localhost:5432/releasehive</property>
<property name="hibernate.connection.username">relhive</property>
<property name="hibernate.connection.password">development</property>
<!-- SQL dialect -->
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Print executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Update database on startup -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- Annotated entity classes -->
<mapping class="wtf.beatrice.releasehive.models.User"/>
</session-factory>
</hibernate-configuration>

10
suppressions.xml Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<suppress>
<notes><![CDATA[
file name: snakeyaml-1.33.jar
]]></notes>
<packageUrl regex="true">^pkg:maven/org\.yaml/snakeyaml@.*$</packageUrl>
<cve>CVE-2021-4235</cve>
</suppress>
</suppressions>