Skip to content

Commit

Permalink
Feature - add interactive login page (#1)
Browse files Browse the repository at this point in the history
* serve login html when login prompt is specified
* formatting, refactoring, and fix failing test for interactive login
* set interactivelogin=true as default for standalone server
* show debug info on login page
* enqueue MockResponse and use this if added instead of OAuth2HttpRequestHandler
  • Loading branch information
tommytroen authored Mar 21, 2020
1 parent df4afcf commit fb191cc
Show file tree
Hide file tree
Showing 27 changed files with 1,013 additions and 420 deletions.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ val jacksonVersion = "2.10.1"
val junitJupiterVersion = "5.5.2"
val konfigVersion = "1.6.10.0"
val kotlinVersion = "1.3.61"

val freemarkerVersion = "2.3.29"
val mavenRepoBaseUrl = "https://oss.sonatype.org"
val mainClassKt = "no.nav.security.mock.StandaloneMockOAuth2ServerKt"

Expand Down Expand Up @@ -54,6 +54,7 @@ dependencies {
api("com.nimbusds:oauth2-oidc-sdk:$nimbusSdkVersion")
implementation("io.github.microutils:kotlin-logging:$kotlinLoggingVersion")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
implementation("org.freemarker:freemarker:$freemarkerVersion")
testImplementation("org.assertj:assertj-core:$assertjVersion")
testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion")
Expand Down
76 changes: 0 additions & 76 deletions src/main/kotlin/no/nav/security/mock/MockOAuth2Server.kt

This file was deleted.

This file was deleted.

This file was deleted.

106 changes: 106 additions & 0 deletions src/main/kotlin/no/nav/security/mock/oauth2/MockOAuth2Server.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package no.nav.security.mock.oauth2

import com.nimbusds.jwt.SignedJWT
import com.nimbusds.oauth2.sdk.AuthorizationCode
import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant
import com.nimbusds.oauth2.sdk.TokenRequest
import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic
import com.nimbusds.oauth2.sdk.auth.Secret
import com.nimbusds.oauth2.sdk.id.ClientID
import mu.KotlinLogging
import no.nav.security.mock.oauth2.extensions.asOAuth2HttpRequest
import no.nav.security.mock.oauth2.extensions.toAuthorizationEndpointUrl
import no.nav.security.mock.oauth2.extensions.toJwksUrl
import no.nav.security.mock.oauth2.extensions.toTokenEndpointUrl
import no.nav.security.mock.oauth2.extensions.toWellKnownUrl
import no.nav.security.mock.oauth2.http.OAuth2HttpRequestHandler
import no.nav.security.mock.oauth2.http.OAuth2HttpResponse
import no.nav.security.mock.oauth2.token.OAuth2TokenCallback
import no.nav.security.mock.oauth2.token.OAuth2TokenProvider
import okhttp3.HttpUrl
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import java.io.IOException
import java.net.InetSocketAddress
import java.net.URI
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue

private val log = KotlinLogging.logger {}

class MockOAuth2Server(
config: OAuth2Config = OAuth2Config()
) {
private val mockWebServer: MockWebServer = MockWebServer()
private val tokenProvider: OAuth2TokenProvider =
OAuth2TokenProvider()

var dispatcher: Dispatcher = MockOAuth2Dispatcher(config)

fun start() {
mockWebServer.start()
mockWebServer.dispatcher = dispatcher
}

fun start(port: Int = 0) {
val address = InetSocketAddress(0).address
log.info("attempting to start server on port $port and InetAddress=$address")
mockWebServer.start(address, port)
mockWebServer.dispatcher = dispatcher
}

@Throws(IOException::class)
fun shutdown() {
mockWebServer.shutdown()
}

fun url(path: String): HttpUrl = mockWebServer.url(path)
fun enqueueResponse(response: MockResponse) = (dispatcher as MockOAuth2Dispatcher).enqueueResponse(response)
fun enqueueCallback(oAuth2TokenCallback: OAuth2TokenCallback) = (dispatcher as MockOAuth2Dispatcher).enqueueTokenCallback(oAuth2TokenCallback)
fun takeRequest(): RecordedRequest = mockWebServer.takeRequest()

fun wellKnownUrl(issuerId: String): HttpUrl = mockWebServer.url(issuerId).toWellKnownUrl()
fun tokenEndpointUrl(issuerId: String): HttpUrl = mockWebServer.url(issuerId).toTokenEndpointUrl()
fun jwksUrl(issuerId: String): HttpUrl = mockWebServer.url(issuerId).toJwksUrl()
fun issuerUrl(issuerId: String): HttpUrl = mockWebServer.url(issuerId)
fun authorizationEndpointUrl(issuerId: String): HttpUrl = mockWebServer.url(issuerId).toAuthorizationEndpointUrl()
fun baseUrl(): HttpUrl = mockWebServer.url("")

fun issueToken(issuerId: String, clientId: String, OAuth2TokenCallback: OAuth2TokenCallback): SignedJWT {
val uri = tokenEndpointUrl(issuerId)
val issuerUrl = issuerUrl(issuerId)
val tokenRequest = TokenRequest(
uri.toUri(),
ClientSecretBasic(ClientID(clientId), Secret("secret")),
AuthorizationCodeGrant(AuthorizationCode("123"), URI.create("http://localhost"))
)
return tokenProvider.accessToken(tokenRequest, issuerUrl, null, OAuth2TokenCallback)
}
}

class MockOAuth2Dispatcher(
config: OAuth2Config
) : Dispatcher() {
private val httpRequestHandler: OAuth2HttpRequestHandler = OAuth2HttpRequestHandler(config)
private val responseQueue: BlockingQueue<MockResponse> = LinkedBlockingQueue()

fun enqueueResponse(mockResponse: MockResponse) = responseQueue.add(mockResponse)
fun enqueueTokenCallback(oAuth2TokenCallback: OAuth2TokenCallback) = httpRequestHandler.enqueueTokenCallback(oAuth2TokenCallback)

override fun dispatch(request: RecordedRequest): MockResponse =
when {
responseQueue.peek() != null -> responseQueue.take()
else -> mockResponse(httpRequestHandler.handleRequest(request.asOAuth2HttpRequest()))
}


private fun mockResponse(response: OAuth2HttpResponse): MockResponse =
MockResponse()
.setHeaders(response.headers)
.setResponseCode(response.status)
.apply {
response.body?.let { this.setBody(it) }
}
}
10 changes: 10 additions & 0 deletions src/main/kotlin/no/nav/security/mock/oauth2/OAuth2Config.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package no.nav.security.mock.oauth2

import no.nav.security.mock.oauth2.token.OAuth2TokenCallback
import no.nav.security.mock.oauth2.token.OAuth2TokenProvider

data class OAuth2Config(
val interactiveLogin: Boolean = false,
val tokenProvider: OAuth2TokenProvider = OAuth2TokenProvider(),
val oAuth2TokenCallbacks: Set<OAuth2TokenCallback> = emptySet()
)
Loading

0 comments on commit fb191cc

Please sign in to comment.