Skip to content

Commit

Permalink
Add authentication to GraphQL API
Browse files Browse the repository at this point in the history
  • Loading branch information
erikhofer committed Oct 25, 2019
1 parent 48fa33d commit 8491993
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 91 deletions.
Expand Up @@ -10,6 +10,8 @@ import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.env.Environment
import org.springframework.core.env.Profiles
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.BeanIds
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
Expand All @@ -28,6 +30,9 @@ class SecurityConfiguration : WebSecurityConfigurerAdapter() {
@Autowired(required = false)
var ldapUserDetailsContextMapper: LdapUserDetailsContextMapper? = null

@Bean(BeanIds.AUTHENTICATION_MANAGER)
override fun authenticationManagerBean(): AuthenticationManager = super.authenticationManagerBean()

override fun configure(http: HttpSecurity?) {
http
?.authorizeRequests()
Expand Down
@@ -1,5 +1,6 @@
package de.code_freak.codefreak.graphql

import com.expediagroup.graphql.annotations.GraphQLID
import com.expediagroup.graphql.annotations.GraphQLName
import com.expediagroup.graphql.spring.operations.Query
import de.code_freak.codefreak.auth.Authority
Expand All @@ -11,7 +12,7 @@ import org.springframework.stereotype.Component
import java.util.UUID

@GraphQLName("Assignment")
class AssignmentDto(val id: UUID, val title: String) {
class AssignmentDto(@GraphQLID val id: UUID, val title: String) {
constructor(assignment: Assignment) : this(assignment.id, assignment.title)
}

Expand Down
31 changes: 31 additions & 0 deletions src/main/kotlin/de/code_freak/codefreak/graphql/AuthApi.kt
@@ -0,0 +1,31 @@
package de.code_freak.codefreak.graphql

import com.expediagroup.graphql.annotations.GraphQLName
import com.expediagroup.graphql.spring.operations.Mutation
import de.code_freak.codefreak.util.FrontendUtil
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY
import org.springframework.stereotype.Component

@GraphQLName("Authentication")
class AuthenticationDto(val token: String, val user: UserDto)

@Component
class AuthMutation : Mutation {

@Autowired(required = false)
private lateinit var authenticationManager: AuthenticationManager

fun login(username: String, password: String): AuthenticationDto {
val auth = authenticationManager.authenticate(UsernamePasswordAuthenticationToken(username, password))
val securityContext = SecurityContextHolder.getContext()
securityContext.authentication = auth
val session = FrontendUtil.getRequest().getSession(true)
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext)

return AuthenticationDto(session.id, UserDto(FrontendUtil.getCurrentUser().entity))
}
}
@@ -1,28 +1,13 @@
package de.code_freak.codefreak.graphql

import com.expediagroup.graphql.hooks.SchemaGeneratorHooks
import graphql.schema.GraphQLScalarType
import graphql.schema.GraphQLType
import org.springframework.stereotype.Component
import java.time.Instant
import java.util.UUID
import kotlin.reflect.KType

@Component
class CustomSchemaGeneratorHooksImpl : SchemaGeneratorHooks {
override fun willGenerateGraphQLType(type: KType): GraphQLType? {
return when (type.classifier) {
UUID::class -> GraphQLScalarType.Builder()
.name("ID")
.description("ID to java.util.UUID")
.coercing(UuidConverter())
.build()
Instant::class -> GraphQLScalarType.Builder()
.name("DateTime")
.description("DateTime to java.time.Instant")
.coercing(DateTimeConverter())
.build()
else -> null
}
return type.classifier?.let { ScalarTypes.get(it) }
}
}
15 changes: 10 additions & 5 deletions src/main/kotlin/de/code_freak/codefreak/graphql/ErrorHandler.kt
Expand Up @@ -6,22 +6,27 @@ import graphql.GraphQLError
import graphql.language.SourceLocation
import graphql.servlet.core.DefaultGraphQLErrorHandler
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.stereotype.Component

@Component
class ErrorHandler : DefaultGraphQLErrorHandler() {

private class AccessDeniedError(val ex: ExceptionWhileDataFetching) : GraphQLError {
override fun getMessage(): String = "Access Denied"
private class CustomError(val ex: ExceptionWhileDataFetching, val customMessage: String, val code: String) : GraphQLError {
override fun getMessage(): String = customMessage
override fun getErrorType(): ErrorClassification = ex.errorType
override fun getLocations(): MutableList<SourceLocation> = ex.locations
override fun getExtensions(): MutableMap<String, Any> = mutableMapOf("errorCode" to "403")
override fun getExtensions(): MutableMap<String, Any> = mutableMapOf("errorCode" to code)
}

override fun processErrors(errors: MutableList<GraphQLError>?): MutableList<GraphQLError> {
val newErrors = errors?.map {
if (it is ExceptionWhileDataFetching && it.exception is AccessDeniedException) {
AccessDeniedError(it)
if (it is ExceptionWhileDataFetching) {
when (it.exception) {
is AccessDeniedException -> CustomError(it, "Access Denied", "403")
is BadCredentialsException -> CustomError(it, "Bad Credentials", "422")
else -> it
}
} else it
}
return super.processErrors(newErrors)
Expand Down
22 changes: 22 additions & 0 deletions src/main/kotlin/de/code_freak/codefreak/graphql/ScalarTypes.kt
@@ -0,0 +1,22 @@
package de.code_freak.codefreak.graphql

import graphql.schema.GraphQLScalarType
import java.time.Instant
import java.util.UUID
import kotlin.reflect.KClassifier

object ScalarTypes {
private val scalars = mapOf(
UUID::class to GraphQLScalarType.Builder()
.name("ID")
.description("ID to java.util.UUID")
.coercing(UuidConverter())
.build(),
Instant::class to GraphQLScalarType.Builder()
.name("DateTime")
.description("DateTime to java.time.Instant")
.coercing(DateTimeConverter())
.build()
)
fun get(classifier: KClassifier) = scalars[classifier]
}
11 changes: 11 additions & 0 deletions src/main/kotlin/de/code_freak/codefreak/graphql/UserApi.kt
@@ -0,0 +1,11 @@
package de.code_freak.codefreak.graphql

import com.expediagroup.graphql.annotations.GraphQLID
import com.expediagroup.graphql.annotations.GraphQLName
import de.code_freak.codefreak.entity.User
import java.util.UUID

@GraphQLName("User")
class UserDto(@GraphQLID val id: UUID, val username: String) {
constructor(user: User) : this(user.id, user.username)
}
69 changes: 0 additions & 69 deletions src/main/resources/graphql/schema.graphqls

This file was deleted.

0 comments on commit 8491993

Please sign in to comment.