generated from CDCgov/template
-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Authentication Microservice POC (#15765)
- Loading branch information
1 parent
8eedc3f
commit efa882c
Showing
20 changed files
with
584 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
11 changes: 11 additions & 0 deletions
11
auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/AuthApplication.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
14 changes: 14 additions & 0 deletions
14
auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/AuthApplicationConstants.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/ApplicationConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) |
35 changes: 35 additions & 0 deletions
35
auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/SecurityConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/controller/AuthController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/model/ApplicationStatus.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) |
55 changes: 55 additions & 0 deletions
55
auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/service/ProxyURIStrategy.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package gov.cdc.prime.reportstream.auth.service | ||
|
||
import gov.cdc.prime.reportstream.auth.config.ApplicationConfig | ||
import org.springframework.context.annotation.Profile | ||
import org.springframework.stereotype.Component | ||
import java.net.URI | ||
|
||
/** | ||
* Implementations are ways to decide the ultimate destination of an incoming request | ||
*/ | ||
interface ProxyURIStrategy { | ||
fun getTargetURI(incomingUri: URI): URI | ||
} | ||
|
||
/** | ||
* This implementation decides via the path prefix. Currently used locally for when all services are | ||
* running on different ports of localhost. | ||
* | ||
* Configured under proxyConfig.pathMappings | ||
* | ||
* http://localhost:9000/submissions/health -> http://localhost:8880/health | ||
*/ | ||
@Component | ||
@Profile("local") | ||
class PathPrefixProxyURIStrategy( | ||
private val applicationConfig: ApplicationConfig, | ||
) : ProxyURIStrategy { | ||
override fun getTargetURI(incomingUri: URI): URI { | ||
val proxyPathMappings = applicationConfig.proxyConfig.pathMappings | ||
val maybePathMapping = proxyPathMappings.find { incomingUri.path.startsWith(it.pathPrefix) } | ||
return if (maybePathMapping != null) { | ||
val baseUri = URI(maybePathMapping.baseUrl) | ||
val path = incomingUri.path.removePrefix(maybePathMapping.pathPrefix) | ||
URI( | ||
baseUri.scheme, | ||
baseUri.userInfo, | ||
baseUri.host, | ||
baseUri.port, | ||
path, | ||
incomingUri.query, | ||
incomingUri.fragment | ||
) | ||
} else { | ||
throw IllegalStateException("no configured proxy target in path mappings for path=${incomingUri.path}") | ||
} | ||
} | ||
} | ||
|
||
@Component | ||
@Profile("deployed") | ||
class HostProxyPathURIStrategy : ProxyURIStrategy { | ||
override fun getTargetURI(incomingUri: URI): URI { | ||
TODO("Not yet implemented") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
spring: | ||
application: | ||
name: "auth" | ||
profiles: | ||
active: local | ||
security: | ||
oauth2: | ||
resourceserver: | ||
opaquetoken: # Set client secret in SPRING_SECURITY_OAUTH2_RESOURCESERVER_OPAQUETOKEN_CLIENT_SECRET env variable | ||
client-id: 0oaek8tip2lhrhHce1d7 | ||
introspection-uri: https://reportstream.oktapreview.com/oauth2/ausekaai7gUuUtHda1d7/v1/introspect | ||
cloud: | ||
gateway: | ||
proxy: | ||
sensitive: [] # pass authorization and cookie headers downstream (filtered by default) | ||
|
||
server.port: 9000 | ||
|
||
proxy.pathMappings: | ||
- pathPrefix: /reportstream | ||
baseUrl: http://localhost:7071 | ||
- pathPrefix: /submissions | ||
baseUrl: http://localhost:8880 | ||
|
||
#Uncomment for verbose logging | ||
#logging: | ||
# level: | ||
# web: debug | ||
# org.springframework.web: debug |
Oops, something went wrong.