Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deployment of 2024-09-19 #15922

Merged
merged 10 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .environment/docker/docker-compose/Dockerfile.azurite
Original file line number Diff line number Diff line change
@@ -1 +1 @@
FROM mcr.microsoft.com/azure-storage/azurite:3.31.0
FROM mcr.microsoft.com/azure-storage/azurite:3.32.0
2 changes: 1 addition & 1 deletion .github/actions/build-vars/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ runs:
echo "has_frontend_change=${{ steps.filter.outputs.frontend_react }}" >> $GITHUB_OUTPUT
fi

- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
if: inputs.sp-creds != 'false'
with:
creds: ${{ inputs.sp-creds }}
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/vpn-azure/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ runs:
fi
shell: bash

- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
if: inputs.sp-creds
with:
creds: ${{ inputs.sp-creds }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release_chatops_app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
with:
submodules: true

- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/restore_databases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ jobs:
echo "SINK_BACKUP_STORAGE=pdh${{ env.SINK_ENV_NAME }}terraform" >> $GITHUB_ENV

# Login to Azure
- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}

Expand Down Expand Up @@ -139,7 +139,7 @@ jobs:
- name: Check out changes
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}

Expand Down Expand Up @@ -230,7 +230,7 @@ jobs:
- name: Check out changes
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/start_test_servers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

# Login to Azure
- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/stop_test_servers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
sp-creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}
tf-auth: true
# Login to Azure
- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/validate_resources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
- name: Check Out Changes
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}

Expand Down Expand Up @@ -136,7 +136,7 @@ jobs:
- name: Check Out Changes
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }}

Expand Down
40 changes: 40 additions & 0 deletions auth/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

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

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

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

### VS Code ###
.vscode/

### Kotlin ###
.kotlin
58 changes: 58 additions & 0 deletions auth/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
apply(from = rootProject.file("buildSrc/shared.gradle.kts"))

plugins {
id("org.springframework.boot") version "3.3.2"
id("io.spring.dependency-management") version "1.1.6"
id("reportstream.project-conventions")
kotlin("plugin.spring") version "2.0.0"
}

group = "gov.cdc.prime"
version = "0.0.1-SNAPSHOT"

dependencies {
implementation(project(":shared"))

implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.8.1")

/**
* Spring WebFlux was chosen for this project to be able to better handle periods of high traffic
*/
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.cloud:spring-cloud-gateway-webflux")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")

runtimeOnly("com.nimbusds:oauth2-oidc-sdk:11.18")

testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
testImplementation("com.squareup.okhttp3:mockwebserver:4.12.0")

testRuntimeOnly("org.junit.platform:junit-platform-launcher")

compileOnly("org.springframework.boot:spring-boot-devtools")
}

// There is a conflict in logging implementations. Excluded these in favor of using log4j-slf4j2-impl
configurations.all {
exclude(group = "org.apache.logging.log4j", module = "log4j-to-slf4j")
exclude(group = "ch.qos.logback")
}

dependencyManagement {
imports {
mavenBom("com.azure.spring:spring-cloud-azure-dependencies:5.14.0")
mavenBom("org.springframework.cloud:spring-cloud-dependencies:2023.0.3")
}
}

kotlin {
compilerOptions {
// https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-kotlin.html#boot-features-kotlin-null-safety
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
Binary file added auth/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions auth/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gov.cdc.prime.reportstream.auth

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class AuthApplication

fun main(args: Array<String>) {
runApplication<AuthApplication>(*args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package gov.cdc.prime.reportstream.auth

/**
* File used for application-wide constants
*/
object AuthApplicationConstants {

/**
* All endpoints defined here
*/
object Endpoints {
const val HEALTHCHECK_ENDPOINT_V1 = "/api/v1/healthcheck"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package gov.cdc.prime.reportstream.auth.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import kotlin.time.TimeSource

/**
* Simple class to automatically read configuration from application.yml (or environment variable overrides)
*/
@Configuration
@EnableConfigurationProperties(ProxyConfigurationProperties::class)
class ApplicationConfig(
val proxyConfig: ProxyConfigurationProperties,
) {

@Bean
fun timeSource(): TimeSource {
return TimeSource.Monotonic
}
}

@ConfigurationProperties("proxy")
data class ProxyConfigurationProperties(
val pathMappings: List<ProxyPathMapping>,
)

data class ProxyPathMapping(
val baseUrl: String,
val pathPrefix: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package gov.cdc.prime.reportstream.auth.config

import gov.cdc.prime.reportstream.auth.AuthApplicationConstants
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.server.SecurityWebFilterChain

/**
* Security configuration setup
*
* All incoming requests will require authentication via opaque token check
*/
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {

@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
http
.authorizeExchange { authorize ->
authorize
// allow health endpoint without authentication
.pathMatchers(AuthApplicationConstants.Endpoints.HEALTHCHECK_ENDPOINT_V1).permitAll()
// all other requests must be authenticated
.anyExchange().authenticated()
}
.oauth2ResourceServer {
it.opaqueToken { }
}

return http.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package gov.cdc.prime.reportstream.auth.controller

import gov.cdc.prime.reportstream.auth.service.ProxyURIStrategy
import kotlinx.coroutines.reactive.awaitSingle
import org.apache.logging.log4j.kotlin.Logging
import org.springframework.cloud.gateway.webflux.ProxyExchange
import org.springframework.http.ResponseEntity
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ServerWebExchange

@RestController
class AuthController(
private val proxyURIStrategy: ProxyURIStrategy,
) : Logging {

/**
* Main workhorse of the application. Handles all incoming requests and properly forwards them given successful
* authentication. Missing or invalid bearer tokens will result in a 401 unauthorized response.
*
* Authentication will be handled by the OAuth 2.0 resource server opaque token configuration
* @see https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/opaque-token.html
*
* Proxying will be handled by the Spring Cloud Gateway library from which the ProxyExchange object is injected
*/
@RequestMapping("**")
suspend fun proxy(
exchange: ServerWebExchange,
proxy: ProxyExchange<ByteArray>,
auth: BearerTokenAuthentication,
): ResponseEntity<ByteArray> {
val sub = auth.tokenAttributes["sub"]
val scopes = auth.tokenAttributes["scope"]

logger.info("Token with sub=$sub and scopes=$scopes is authenticated with Okta")

val uri = proxyURIStrategy.getTargetURI(exchange.request.uri)
proxy.uri(uri.toString())

logger.info("Proxying request to ${exchange.request.method} $uri")
val response = proxy.forward().awaitSingle()
logger.info("Proxy response from ${exchange.request.method} $uri status=${response.statusCode}")

return response
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package gov.cdc.prime.reportstream.auth.controller

import gov.cdc.prime.reportstream.auth.AuthApplicationConstants
import gov.cdc.prime.reportstream.auth.model.ApplicationStatus
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import kotlin.time.TimeSource

@RestController
class HealthController(
timeSource: TimeSource,
) {

private val applicationStart = timeSource.markNow()

@GetMapping(
AuthApplicationConstants.Endpoints.HEALTHCHECK_ENDPOINT_V1,
produces = [MediaType.APPLICATION_JSON_VALUE]
)
suspend fun health(): ApplicationStatus {
val uptime = applicationStart.elapsedNow().toString()
return ApplicationStatus("auth", "ok", uptime)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package gov.cdc.prime.reportstream.auth.model

/**
* Simple json response model for application status
*/
data class ApplicationStatus(
val application: String,
val status: String,
val uptime: String,
)
Loading
Loading