Skip to content

Commit

Permalink
� This is a combination of 81 commits.
Browse files Browse the repository at this point in the history
� This is the 1st commit message:

squash

� The commit message #2 will be skipped:

� Add check for global bans

� The commit message #3 will be skipped:

� It's not hibernate, we can use data classes

� The commit message #4 will be skipped:

� Add login throttling on to many failed attempts

� The commit message #5 will be skipped:

� Make security settings configurable

� The commit message #6 will be skipped:

� Add i18n for forms

� The commit message #7 will be skipped:

� Add register account url to login form

� The commit message #8 will be skipped:

� Package renaming

� The commit message #9 will be skipped:

� Add Github CI pipeline

� The commit message #10 will be skipped:

� Add Jib to build the docker image

� The commit message #11 will be skipped:

� Add fake TLS forwarding to Hydra service

� The commit message #12 will be skipped:

� Add global exception handler

� The commit message #13 will be skipped:

� You Gotta Pump Those Numbers Up
�
� Those Are Rookie Numbers!

� The commit message #14 will be skipped:

� Port over PasswordEncoder from the api

� The commit message #15 will be skipped:

� Restyle the html pages

� The commit message #16 will be skipped:

� Random background image

� The commit message #17 will be skipped:

� Fix a few styling issues

� The commit message #18 will be skipped:

� Fix consent tooltip text

� The commit message #19 will be skipped:

� Bump Spring Boot to 2.4.2

� The commit message #20 will be skipped:

� Update Ory Hydra model to 1.9.2

� The commit message #21 will be skipped:

� Extend access and id token with user roles
�
� Also: Allow untrusted tls certificates

� The commit message #22 will be skipped:

� Add configuration for secured (sample) endpoint

� The commit message #23 will be skipped:

� Make issue-uri configurable

� The commit message #24 will be skipped:

� Fix build

� The commit message #25 will be skipped:

� Rename env variable to avoid confusion

� The commit message #26 will be skipped:

� Dependency upgrades

� The commit message #27 will be skipped:

� Add integration tests

� The commit message #28 will be skipped:

� Remove UserController (for now)

� The commit message #29 will be skipped:

� Rework Gradle CLI opts

� The commit message #30 will be skipped:

� Rework Gradle CLI opts #2

� The commit message #31 will be skipped:

� Refactor integration tests

� The commit message #32 will be skipped:

� Allow login via Email

� The commit message #33 will be skipped:

� Rename variables for clarification

� The commit message #34 will be skipped:

� Just IntelliJ removing required imports

� The commit message #35 will be skipped:

� Add FAF OAuth scopes to translations

� The commit message #36 will be skipped:

� Update Kotlin to 1.5.0

� The commit message #37 will be skipped:

� Add text for offline scope

� The commit message #38 will be skipped:

� Fix consent tooltips

� The commit message #39 will be skipped:

� Make main card margin responsive

� The commit message #40 will be skipped:

� Make text black

� The commit message #41 will be skipped:

� Make all text colors default to black

� The commit message #42 will be skipped:

� Remove trailing text

� The commit message #43 will be skipped:

� Change margin back to 5rem
�
� Just revert it there isnt a way to make the responsiveness work in the client webview

� The commit message #44 will be skipped:

� More detailed login logs

� The commit message #45 will be skipped:

� Use grid display for oauth scopes
�

� The commit message #46 will be skipped:

� Update to Ory Hydra model to v1.10.2

� The commit message #47 will be skipped:

� Add readme

� The commit message #48 will be skipped:

� Update to Gradle 7.0.2 and Kotlin 1.5.10

� The commit message #49 will be skipped:

� Remove jcenter repository

� The commit message #50 will be skipped:

� Update spotless to 5.12.5

� The commit message #51 will be skipped:

� Bump Spring Boot to 2.5.0

� The commit message #52 will be skipped:

� Use Java 16 for Docker image

� The commit message #53 will be skipped:

� Remove invalid comment

� The commit message #54 will be skipped:

� Bumo ktlint to 0.41.0

� The commit message #55 will be skipped:

� Fix tagging of images from CI

� The commit message #56 will be skipped:

� Fix tagging of images from CI #2

� The commit message #57 will be skipped:

� Update to Spring Boot 2.5.1

� The commit message #58 will be skipped:

� Remove filter from background
�
� This is most likely causing the issues in the client as it forces loading a huge texture for users with high resolution

� The commit message #59 will be skipped:

� Don't throw internal server error on wrong input

� The commit message #60 will be skipped:

� Update Spring Boot to 2.5.2 and Kotlin to 1.5.21

� The commit message #61 will be skipped:

� Add login flash message on throttling (#8)
�
� Closes #7 

� The commit message #62 will be skipped:

� Read real ips from reverse proxy header
�
� Closes #10

� The commit message #63 will be skipped:

� Fix formatting

� The commit message #64 will be skipped:

� Add ability to revoke consent sessions (#9)
�

� The commit message #65 will be skipped:

� Add ban html template (#7)
�

� The commit message #66 will be skipped:

� Add german ban translations

� The commit message #67 will be skipped:

� Add missing scope texts

� The commit message #68 will be skipped:

� Remove Twitter links
�
� Closes #12

� The commit message #69 will be skipped:

� Update to Spring Boot 2.5.4

� The commit message #70 will be skipped:

� Update to Jib plugin to 3.1.4

� The commit message #71 will be skipped:

� Check for steam id if lobby scope is requested (#13)
�
� * Add game ownership check
� 
� * Add logging of rejection for missing game ownership verification

� The commit message #72 will be skipped:

� Use Layout dialect to reduce duplication in Thymeleaf templates (#14)
�

� The commit message #73 will be skipped:

� Update to Kotlin 1.5.30

� The commit message #74 will be skipped:

� Hide consent info text if no translation is available

� The commit message #75 will be skipped:

� Accept gog linked accounts for lobby scope
�
� Requires faf-db v120+

� The commit message #76 will be skipped:

� Mark new column as transient
�
� It does not exist in the database, but without the annotation it's taken as a column causing SQL errors.

� The commit message #77 will be skipped:

� Add date parameter to throttle login query (#18)
�
� * Add date parameter to throttle login query
� 
� * Use configurable parameter for number of days to check

� The commit message #78 will be skipped:

� Handle reloading of already handled login and consent requests
�
� Fixes #16

� The commit message #79 will be skipped:

� Remove code warnings / unused variables

� The commit message #80 will be skipped:

� Use form objects to retrieve form data (#20)
�

� The commit message #81 will be skipped:

� Remove duplicate Gradle test setup
  • Loading branch information
Brutus5000 authored and Sheikah45 committed Jul 16, 2023
1 parent b9e93d1 commit bfd0d73
Show file tree
Hide file tree
Showing 88 changed files with 3,826 additions and 300 deletions.
8 changes: 8 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[*.{kt,kts}]
indent_size=4
ij_continuation_indent_size=4
insert_final_newline=true
max_line_length=120
end_of_line=lf
ij_any_line_comment_add_space = true
ij_any_line_comment_at_first_column = false
27 changes: 27 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Build
on: [push]
jobs:
test:
runs-on: ubuntu-latest
container: adoptopenjdk:11-jdk-hotspot-bionic
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Gradle build
env:
GRADLE_CLI_OPTS: ""
run: "chmod +x ./gradlew && ./gradlew ${GRADLE_CLI_OPTS} build"
- name: Get tag or branch
id: get_tag_or_branch
run: |
if echo $GITHUB_REF | grep -Eq '^refs\/tags\/.*'; then
echo ::set-output name=TAG_OR_BRANCH::${GITHUB_REF#refs/tags/}
else
echo ::set-output name=TAG_OR_BRANCH::${GITHUB_REF#refs/heads/}
fi
- name: Build and push Docker images
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')
env:
GRADLE_CLI_OPTS: "-Djib.to.auth.username=${{secrets.DOCKER_USERNAME}} -Djib.to.auth.password=${{secrets.DOCKER_PASSWORD}} -Djib.to.tags=${{steps.get_tag_or_branch.outputs.TAG_OR_BRANCH}}"
run: "chmod +x ./gradlew && ./gradlew ${GRADLE_CLI_OPTS} jib"

65 changes: 33 additions & 32 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
# Gradle
.gradle/
# The OpenAPI generator files go there
hydra-generated/

HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

# Eclipse
.project
### STS ###
.apt_generated
.classpath
.settings/
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

# IntelliJ
### IntelliJ IDEA ###
.idea
*.ipr
*.iml
*.iws

# NetBeans
nb-configuration.xml

# Visual Studio Code
.vscode
.factorypath

# OSX
.DS_Store

# Vim
*.swp
*.swo

# patch
*.orig
*.rej

# Local environment
.env

# Plugin directory
/.quarkus/cli/plugins/
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/
3 changes: 3 additions & 0 deletions .openapi-generator-ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*
**/*
!**/src/main/kotlin/**/*
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Christian Schmidt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
48 changes: 48 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# FAF User Service

This service aims to cover the domain of login and account management in FAForever.

## Technology stack

- Kotlin
- Spring Boot
- Spring Webflux
- Spring Data R2DBC


## Motivation and architecture considerations

### Yet another FAF API?

> The faf-java-api already offers OAuth2 login and user management. Why do we need yet another service?
There are multiple reasons that led to this decision:

1. **Application perspective:** The faf-java-api uses the
[Spring Security OAuth](https://spring.io/projects/spring-security-oauth) library.
1. With the release of Spring 5 this got deprecated in favour of a newer one. Unfortunately with this transition
the support for OAuth2 identity server was dropped completely. It receives no more updates.
1. Since the library is deprecated we lack support for the improved OAuth2 PKCE login flow, which improves overall
security compared to the previous implicit flow.
1. The current library seems to have issues when trying to login with certified libraries from the Angular ecosystem.
It just doesn't work in some cases, where we need it to work. (But nobody will fix it since it's deprecated.)
2. **Architecture perspective:** The faf-java-api is the FAF swiss army knife. It basically bundles every feature
outside of the lobby server protocol. This makes it very complex to maintain and configure. It also causes very high
startup times causing unnecessary downtimes on deployments. This does not match our desired architecture.
A new microservice focussing on one particular topic (and security is a very important topic which is also hard to get
right) simplifies that.
3. **GDPR and DevOps implications:** Currently FAF runs almost all applications on one server. An admin on that server
has access to all personal data. Adding new admins is a large hassle due to GDPR requirements. Due to this many
FAF maintainers have no access to their application logs and configuration, which makes fixing bugs etc. much more
complicated and adds additional work onto the few admins. This new service might
be a first step into moving the whole account management out of the main server.
4. **Long running perspective:** In a perfect world we would migrate all authorization related stuff into a dedicated
(trusted) 3rd party software, so we can't mess up on security.

### Additional goals

Goal | Status
---- | ------
Usability improvements by serving translated web pages | :heavy_check_mark:
Improved performance by using reactive stack | :heavy_check_mark:
Massively reduced startup times and smaller resource footprint by compiling to native images with GraalVM | :hourglass:
190 changes: 141 additions & 49 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,67 +1,159 @@
import com.google.cloud.tools.jib.gradle.JibExtension
import com.google.cloud.tools.jib.gradle.JibPlugin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

val dockerTag: String? by project

plugins {
kotlin("jvm") version "1.8.21"
kotlin("plugin.allopen") version "1.8.21"
id("io.quarkus")
val kotlinVersion = "1.5.30"

kotlin("jvm") version kotlinVersion
kotlin("kapt") version kotlinVersion
kotlin("plugin.spring") version kotlinVersion
id("org.springframework.boot") version "2.5.4"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
id("com.google.cloud.tools.jib") version "3.1.4"

// /****** Additional tooling *****/
// // OpenAPI code generation
// id("org.openapi.generator") version "4.3.1"
// Code formatting
id("com.diffplug.spotless") version "5.12.5"
}

group = "com.faforever"
version = "snapshot"
java.sourceCompatibility = JavaVersion.VERSION_11

repositories {
mavenCentral()
mavenLocal()
maven("https://jitpack.io")
}

val quarkusPlatformGroupId: String by project
val quarkusPlatformArtifactId: String by project
val quarkusPlatformVersion: String by project

dependencies {
implementation("io.quarkus:quarkus-rest-client-reactive-jackson")
implementation("io.quarkus:quarkus-config-yaml")
implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
implementation("io.quarkus:quarkus-hibernate-validator")
implementation("io.quarkus:quarkus-resteasy-reactive-qute")
implementation("io.quarkus:quarkus-resteasy-reactive-jackson")
implementation("io.quarkus:quarkus-hibernate-orm-panache-kotlin")
implementation("io.quarkus:quarkus-qute")
implementation("io.quarkus:quarkus-csrf-reactive")
implementation("io.quarkus:quarkus-jdbc-mariadb")
implementation("io.quarkus:quarkus-kotlin")
implementation("io.quarkus:quarkus-container-image-jib")
kapt("org.springframework.boot:spring-boot-configuration-processor")
implementation("org.springframework.boot:spring-boot-configuration-processor")
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.security:spring-security-oauth2-resource-server")
implementation("org.springframework.security:spring-security-oauth2-jose")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity5")
implementation("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("io.quarkus:quarkus-arc")
implementation("io.quarkus:quarkus-hibernate-orm")
implementation("io.quarkus:quarkus-resteasy-reactive")
testImplementation("io.quarkus:quarkus-junit5")
testImplementation("io.rest-assured:rest-assured")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("io.swagger.core.v3:swagger-annotations:2.1.6")
runtimeOnly("dev.miku:r2dbc-mysql")
runtimeOnly("mysql:mysql-connector-java")
testImplementation("org.springframework.security:spring-security-oauth2-client")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.mockito.kotlin:mockito-kotlin:3.1.0")
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.mock-server:mockserver-netty:5.11.2")
testImplementation("org.mock-server:mockserver-client-java:5.11.2")
}

group = "com.faforever"
version = "1.0.0-SNAPSHOT"

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}

tasks.withType<Test> {
systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
spotless {
val ktlintVersion = "0.41.0"
kotlin {
ktlint(ktlintVersion)
}
kotlinGradle {
target("*.gradle.kts")
ktlint(ktlintVersion)
}
}

/**
* Open Kotlin (data) classes and provide no-args constructor for Java compatibility
*/
allOpen {
// Quarkus
annotation("jakarta.ws.rs.Path")
annotation("jakarta.enterprise.context.ApplicationScoped")
annotation("io.quarkus.test.junit.QuarkusTest")

// Hibernate
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
plugins.withType<JibPlugin> {
configure<JibExtension> {

from.image = "adoptopenjdk:16-jre-hotspot"

to {
image = "faforever/faf-user-service"
}
}
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = JavaVersion.VERSION_17.toString()
kotlinOptions.javaParameters = true
tasks.withType<Test> {
useJUnitPlatform()

// Summarize test results
addTestListener(object : TestListener {
val ANSI_BOLD_WHITE = "\u001B[0;1m"
val ANSI_RESET = "\u001B[0m"
val ANSI_BLACK = "\u001B[30m"
val ANSI_RED = "\u001B[31m"
val ANSI_GREEN = "\u001B[32m"
val ANSI_YELLOW = "\u001B[33m"
val ANSI_BLUE = "\u001B[34m"
val ANSI_PURPLE = "\u001B[35m"
val ANSI_CYAN = "\u001B[36m"
val ANSI_WHITE = "\u001B[37m"
val BALLOT_CHECKED = "\uD83D\uDDF9"
val BALLOT_UNCHECKED = "\u2610"
val BALLOT_CROSS = "\uD83D\uDDF7"

override fun beforeSuite(suite: TestDescriptor) {
if (suite.name.startsWith("Test Run") || suite.name.startsWith("Gradle Worker")) {
return
}

if (suite.parent != null && suite.className != null) {
println(ANSI_BOLD_WHITE + suite.name + ANSI_RESET)
}
}

override fun beforeTest(testDescriptor: TestDescriptor?) {
}

override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {
val indicator = when {
result.failedTestCount > 0 -> ANSI_RED + BALLOT_CROSS
result.skippedTestCount > 0 -> ANSI_YELLOW + BALLOT_UNCHECKED
else -> ANSI_GREEN + BALLOT_CHECKED
}

println(" $indicator$ANSI_RESET ${testDescriptor.name}")
}

override fun afterSuite(suite: TestDescriptor, result: TestResult) {
if (suite.parent != null && suite.className != null) {
println("")
}

if (suite.parent == null) { // will match the outermost suite
val successStyle = ANSI_GREEN
val skipStyle = ANSI_YELLOW
val failStyle = ANSI_RED
val summaryStyle = when (result.resultType) {
TestResult.ResultType.SUCCESS -> successStyle
TestResult.ResultType.SKIPPED -> skipStyle
TestResult.ResultType.FAILURE -> failStyle
}

println(
"""
--------------------------------------------------------------------------
Results: $summaryStyle${result.resultType}$ANSI_RESET (${result.testCount} tests, $successStyle${result.successfulTestCount} passed$ANSI_RESET, $failStyle${result.failedTestCount} failed$ANSI_RESET, $skipStyle${result.skippedTestCount} skipped$ANSI_RESET)
--------------------------------------------------------------------------
""".trimIndent()
)
}
}
})
}
Loading

0 comments on commit bfd0d73

Please sign in to comment.