Skip to content

Commit

Permalink
0.0.8
Browse files Browse the repository at this point in the history
  • Loading branch information
babyfish-ct committed Apr 20, 2022
1 parent d7426c8 commit 964af48
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 93 deletions.
6 changes: 3 additions & 3 deletions example/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ plugins {
}

group = "org.babyfish.graphql.provider"
version = "0.0.7"
version = "0.0.8"

repositories {
mavenCentral()
}

dependencies {
implementation("org.babyfish.graphql.provider:graphql-provider-starter-dgs:0.0.7")
ksp("org.babyfish.kimmer:kimmer-ksp:0.3.1")
implementation("org.babyfish.graphql.provider:graphql-provider-starter-dgs:0.0.8")
ksp("org.babyfish.kimmer:kimmer-ksp:0.3.3")
runtimeOnly("io.r2dbc:r2dbc-h2:0.8.5.RELEASE")
}

Expand Down
Binary file modified project/.DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion project/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
allprojects {
group = "org.babyfish.graphql.provider"
version = "0.0.7"
version = "0.0.8"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import org.babyfish.graphql.provider.meta.MetaProvider
import org.babyfish.graphql.provider.runtime.DataFetchers
import org.babyfish.graphql.provider.runtime.cfg.GraphQLProviderProperties
import org.babyfish.graphql.provider.runtime.registryDynamicCodeRegistry
import org.babyfish.graphql.provider.security.AuthenticationExtractor
import org.babyfish.graphql.provider.security.jwt.JwtAuthenticationService

@DgsComponent
internal open class DynamicCodeRegistry(
private val properties: GraphQLProviderProperties,
private val dataFetchers: DataFetchers,
private val metaProvider: MetaProvider,
private val jwtAuthenticationService: JwtAuthenticationService?
private val jwtAuthenticationService: JwtAuthenticationService?,
private val authenticationExtractor: AuthenticationExtractor?
) {
@DgsCodeRegistry
open fun registry(
Expand All @@ -27,7 +29,8 @@ internal open class DynamicCodeRegistry(
properties,
dataFetchers,
metaProvider,
jwtAuthenticationService
jwtAuthenticationService,
authenticationExtractor
)
}
}
3 changes: 0 additions & 3 deletions project/graphql-provider/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-data-r2dbc:2.6.6")
testImplementation("org.springframework.boot:spring-boot-starter-webflux:2.6.6")
testRuntimeOnly("io.r2dbc:r2dbc-h2:0.8.5.RELEASE")

implementation("org.springdoc:springdoc-openapi-kotlin:1.6.7")
implementation("org.springdoc:springdoc-openapi-webflux-ui:1.6.7")
}

ksp {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@ internal fun createAuthenticationSDLDefinitions(
inputValueDefinition(InputValueDefinition("password", TypeName("String")))
}.build()
}
api.updatePassword.trim().takeIf { it.isNotEmpty() }?.let {
mutationFields += FieldDefinition.newFieldDefinition().apply {
api.refreshAccessToken.trim().takeIf { it.isNotEmpty() }?.let {
queryFields += FieldDefinition.newFieldDefinition().apply {
name(it)
type(TypeName(AUTHENTICATION_RESULT))
inputValueDefinition(InputValueDefinition("oldPassword", TypeName("String")))
inputValueDefinition(InputValueDefinition("newPassword", TypeName("String")))
inputValueDefinition(InputValueDefinition("refreshToken", TypeName("String")))
}.build()
}
api.refreshAccessToken.trim().takeIf { it.isNotEmpty() }?.let {
api.updatePassword.trim().takeIf { it.isNotEmpty() }?.let {
mutationFields += FieldDefinition.newFieldDefinition().apply {
name(it)
type(TypeName(AUTHENTICATION_RESULT))
inputValueDefinition(InputValueDefinition("refreshToken", TypeName("String")))
inputValueDefinition(InputValueDefinition("oldPassword", TypeName("String")))
inputValueDefinition(InputValueDefinition("newPassword", TypeName("String")))
}.build()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ package org.babyfish.graphql.provider.runtime
import graphql.schema.DataFetcher
import graphql.schema.FieldCoordinates
import graphql.schema.GraphQLCodeRegistry
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.reactor.mono
import org.babyfish.graphql.provider.meta.MetaProvider
import org.babyfish.graphql.provider.runtime.cfg.GraphQLProviderProperties
import org.babyfish.graphql.provider.security.AuthenticationExtractor
import org.babyfish.graphql.provider.security.jwt.JwtAuthenticationService

fun GraphQLCodeRegistry.Builder.registryDynamicCodeRegistry(
properties: GraphQLProviderProperties,
dataFetchers: DataFetchers,
metaProvider: MetaProvider,
jwtAuthenticationService: JwtAuthenticationService?
jwtAuthenticationService: JwtAuthenticationService?,
authenticationExtractor: AuthenticationExtractor?
) {
registerAuthenticationApi(properties, jwtAuthenticationService)
registerAuthenticationApi(
properties,
jwtAuthenticationService,
authenticationExtractor
)
for (prop in metaProvider.queryType.props.values) {
val coordinates = FieldCoordinates.coordinates("Query", prop.name)
val dataFetcher = DataFetcher {
Expand Down Expand Up @@ -49,38 +53,39 @@ fun GraphQLCodeRegistry.Builder.registryDynamicCodeRegistry(

private fun GraphQLCodeRegistry.Builder.registerAuthenticationApi(
properties: GraphQLProviderProperties,
jwtAuthenticationService: JwtAuthenticationService?
jwtAuthenticationService: JwtAuthenticationService?,
authenticationExtractor: AuthenticationExtractor?
) {
val api = properties.security.api
if (api.graphql && jwtAuthenticationService !== null) {
api.login.trim().takeIf { it.isNotEmpty() }?.let {
val coordinates = FieldCoordinates.coordinates("Query", it)
val dataFetcher = DataFetcher {
mono(Dispatchers.Unconfined) {
api.login.trim().takeIf { it.isNotEmpty() }?.let { fn ->
val coordinates = FieldCoordinates.coordinates("Query", fn)
val dataFetcher = DataFetcher { it
graphqlMono(ExecutorContext(null, it, authenticationExtractor?.get(it))) {
val username = it.getArgument<String>(api.usernameArgName)
val password = it.getArgument<String>("password")
jwtAuthenticationService.login(username, password)
}.toFuture()
}
dataFetcher(coordinates, dataFetcher)
}
api.updatePassword.trim().takeIf { it.isNotEmpty() }?.let {
val coordinates = FieldCoordinates.coordinates("Mutation", it)
api.refreshAccessToken.trim().takeIf { it.isNotEmpty() }?.let { fn ->
val coordinates = FieldCoordinates.coordinates("Query", fn)
val dataFetcher = DataFetcher {
mono(Dispatchers.Unconfined) {
val oldPassword = it.getArgument<String>("oldPassword")
val newPassword = it.getArgument<String>("newPassword")
jwtAuthenticationService.updatePassword(oldPassword, newPassword)
graphqlMono(ExecutorContext(null, it, authenticationExtractor?.get(it))) {
val refreshToken = it.getArgument<String>("refreshToken")
jwtAuthenticationService.refreshAccessToken(refreshToken)
}.toFuture()
}
dataFetcher(coordinates, dataFetcher)
}
api.refreshAccessToken.trim().takeIf { it.isNotEmpty() }?.let {
val coordinates = FieldCoordinates.coordinates("Mutation", it)
api.updatePassword.trim().takeIf { it.isNotEmpty() }?.let { fn ->
val coordinates = FieldCoordinates.coordinates("Mutation", fn)
val dataFetcher = DataFetcher {
mono(Dispatchers.Unconfined) {
val refreshToken = it.getArgument<String>("refreshToken")
jwtAuthenticationService.refreshAccessToken(refreshToken)
graphqlMono(ExecutorContext(null, it, authenticationExtractor?.get(it))) {
val oldPassword = it.getArgument<String>("oldPassword")
val newPassword = it.getArgument<String>("newPassword")
jwtAuthenticationService.updatePassword(oldPassword, newPassword)
}.toFuture()
}
dataFetcher(coordinates, dataFetcher)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ internal open class JwtAuthenticationServiceImpl(
newPassword: String
): JwtAuthenticationResult {
authenticationBehaviorProvider.validateRawPassword(newPassword)
val user = authenticationOrNull()?.details as UserDetails?
val user = authenticationOrNull()?.principal as? UserDetails
?: throw JwtUpdatePasswordException(JwtUpdatePasswordException.Reason.UNAUTHENTICATED)
if (!matches(oldPassword, user.password)) {
throw JwtUpdatePasswordException(JwtUpdatePasswordException.Reason.ILLEGAL_OLD_PASSWORD)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
package org.babyfish.graphql.provider.security.jwt.cfg

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import kotlinx.coroutines.reactor.awaitSingle
import org.babyfish.graphql.provider.runtime.ExecutorContext
import org.babyfish.graphql.provider.runtime.cfg.GraphQLProviderProperties
import org.babyfish.graphql.provider.runtime.withExecutorContext
import org.babyfish.graphql.provider.security.cfg.toGraphQLError
import org.babyfish.graphql.provider.security.jwt.JwtAuthenticationManager
import org.babyfish.graphql.provider.security.jwt.JwtAuthenticationService
import org.babyfish.graphql.provider.security.jwt.JwtTokenConverter
import org.springdoc.core.GroupedOpenApi
import org.springdoc.core.annotations.RouterOperation
import org.springdoc.core.annotations.RouterOperations
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.bodyValueAndAwait
import org.springframework.web.reactive.function.server.coRouter
Expand All @@ -26,25 +15,10 @@ import org.springframework.web.reactive.function.server.coRouter
@ConditionalOnExpression(RestApiConfiguration.SPEL)
internal open class RestApiConfiguration(
private val properties: GraphQLProviderProperties,
private val jwtAuthenticationService: JwtAuthenticationService,
private val jwtTokenConverter: JwtTokenConverter,
private val jwtAuthenticationManager: JwtAuthenticationManager
private val jwtAuthenticationService: JwtAuthenticationService
) {

@Bean
@RouterOperations(
RouterOperation(
path = "$DOC_BASE/login",
method = arrayOf(RequestMethod.GET),
parameterTypes = [String::class, String::class],
operation = Operation(
parameters = [
Parameter(name = "username", required = true),
Parameter(name = "password", required = true)
]
)
)
)
open fun authenticationRouter() =
coRouter {
val api = properties.security.api
Expand All @@ -65,29 +39,8 @@ internal open class RestApiConfiguration(
}
}
}
api.updatePassword.trim().takeIf { it.isNotEmpty() }?.let { fn->
PUT("${api.restPath}/$fn") {
val jwtToken = jwtTokenConverter.convert(it.exchange()).awaitSingle()
val authentication = jwtAuthenticationManager.authenticate(jwtToken).awaitSingle()
withExecutorContext(ExecutorContext(null, null, authentication)) {
val oldPassword = it.queryParam("oldPassword").get()
val newPassword = it.queryParam("newPassword").get()
try {
ServerResponse.ok()
.bodyValueAndAwait(
jwtAuthenticationService.updatePassword(oldPassword, newPassword)
)
} catch (ex: Throwable) {
ServerResponse.status(HttpStatus.BAD_GATEWAY)
.bodyValueAndAwait(
ex.toGraphQLError()
)
}
}
}
}
api.refreshAccessToken.trim().takeIf { it.isNotEmpty() }?.let { fn->
PUT("${api.refreshAccessToken}/$fn") {
GET("${api.restPath}/$fn") {
val refreshToken = it.queryParam("refreshToken").get()
try {
ServerResponse.ok()
Expand All @@ -104,14 +57,6 @@ internal open class RestApiConfiguration(
}
}

@Bean
open fun storeOpenApi(): GroupedOpenApi? {
val path = properties.security.api.restPath
val paths = arrayOf("/${path}/**")
return GroupedOpenApi.builder().group(path).pathsToMatch(*paths)
.build()
}

companion object {

private const val REST_PATH = "restPath"
Expand Down

0 comments on commit 964af48

Please sign in to comment.