Skip to content
This repository has been archived by the owner on Oct 18, 2024. It is now read-only.

Commit

Permalink
feat: authorization (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
brizzbuzz authored Feb 27, 2023
1 parent 47c24e1 commit e3c49df
Show file tree
Hide file tree
Showing 146 changed files with 2,198 additions and 1,335 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ object Members {
val deleteMember = MemberName("io.ktor.server.routing", "delete")
val callMember = MemberName("io.ktor.server.application", "call")
val receiveMember = MemberName("io.ktor.server.request", "receive")
val principalMember = MemberName("io.ktor.server.auth", "principal")
val respondMember = MemberName("io.ktor.server.response", "respond")
val installMember = MemberName("io.ktor.server.application", "install")
val getAllParametersMember = MemberName("io.bkbn.lerasium.api.util.ApiDocumentationUtils", "getAllParameters")
Expand All @@ -22,4 +23,5 @@ object Members {
val ktorJsonMember = MemberName("io.ktor.serialization.kotlinx.json", "json")
val kotlinxJsonMember = MemberName("kotlinx.serialization.json", "Json")
val authenticationMember = MemberName("io.ktor.server.auth", "authentication")
val toRequestContextMember = MemberName("io.bkbn.lerasium.api.util.RequestContextUtils", "toContext")
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package io.bkbn.lerasium.api.processor.visitor

import com.google.devtools.ksp.KspExperimental
Expand All @@ -22,14 +21,16 @@ import io.bkbn.lerasium.api.processor.Members.callMember
import io.bkbn.lerasium.api.processor.Members.deleteMember
import io.bkbn.lerasium.api.processor.Members.getMember
import io.bkbn.lerasium.api.processor.Members.postMember
import io.bkbn.lerasium.api.processor.Members.principalMember
import io.bkbn.lerasium.api.processor.Members.putMember
import io.bkbn.lerasium.api.processor.Members.receiveMember
import io.bkbn.lerasium.api.processor.Members.respondMember
import io.bkbn.lerasium.api.processor.Members.routeMember
import io.bkbn.lerasium.api.processor.Members.toRequestContextMember
import io.bkbn.lerasium.api.processor.authSlug
import io.bkbn.lerasium.api.processor.hasQueries
import io.bkbn.lerasium.core.Relation
import io.bkbn.lerasium.core.model.LoginRequest
import io.bkbn.lerasium.core.request.AnonymousRequestContext
import io.bkbn.lerasium.utils.KotlinPoetUtils.addCodeBlock
import io.bkbn.lerasium.utils.KotlinPoetUtils.addControlFlow
import io.bkbn.lerasium.utils.KotlinPoetUtils.toApiDocumentationClass
Expand All @@ -39,6 +40,7 @@ import io.bkbn.lerasium.utils.StringUtils.capitalized
import io.bkbn.lerasium.utils.StringUtils.decapitalized
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.server.auth.jwt.JWTPrincipal
import io.ktor.server.routing.Route
import java.util.Locale
import java.util.UUID
Expand Down Expand Up @@ -152,13 +154,14 @@ class ControllerVisitor(private val fileBuilder: FileSpec.Builder, private val l
private fun CodeBlock.Builder.addCreateRoute(charter: LerasiumCharter) {
add(CodeBlock.builder().apply {
addControlFlow("%M", postMember) {
addContextCall()
addStatement(
"val request = %M.%M<%T>()",
callMember,
receiveMember,
charter.apiCreateRequestClass
)
addStatement("val result = %T.create(request)", charter.apiServiceClass)
addStatement("val result = %T.create(context, request)", charter.apiServiceClass)
addStatement("%M.%M(result)", callMember, respondMember)
}
}.build())
Expand All @@ -167,8 +170,9 @@ class ControllerVisitor(private val fileBuilder: FileSpec.Builder, private val l
private fun CodeBlock.Builder.addReadRoute(charter: LerasiumCharter) {
add(CodeBlock.builder().apply {
addControlFlow("%M", getMember) {
addContextCall()
addStatement("val id = %T.fromString(%M.parameters[%S])", UUID::class, callMember, "id")
addStatement("val result = %T.read(id)", charter.apiServiceClass)
addStatement("val result = %T.read(context, id)", charter.apiServiceClass)
addStatement("%M.%M(result)", callMember, respondMember)
}
}.build())
Expand All @@ -177,9 +181,10 @@ class ControllerVisitor(private val fileBuilder: FileSpec.Builder, private val l
private fun CodeBlock.Builder.addUpdateRoute(charter: LerasiumCharter) {
add(CodeBlock.builder().apply {
addControlFlow("%M", putMember) {
addContextCall()
addStatement("val id = %T.fromString(%M.parameters[%S])", UUID::class, callMember, "id")
addStatement("val request = %M.%M<%T>()", callMember, receiveMember, charter.apiUpdateRequestClass)
addStatement("val result = %T.update(id, request)", charter.apiServiceClass)
addStatement("val result = %T.update(context, id, request)", charter.apiServiceClass)
addStatement("%M.%M(result)", callMember, respondMember)
}
}.build())
Expand All @@ -188,8 +193,9 @@ class ControllerVisitor(private val fileBuilder: FileSpec.Builder, private val l
private fun CodeBlock.Builder.addDeleteRoute(charter: LerasiumCharter) {
add(CodeBlock.builder().apply {
addControlFlow("%M", deleteMember) {
addContextCall()
addStatement("val id = %T.fromString(%M.parameters[%S])", UUID::class, callMember, "id")
addStatement("%T.delete(id)", charter.apiServiceClass)
addStatement("%T.delete(context, id)", charter.apiServiceClass)
addStatement("%M.%M(%T.NoContent)", callMember, respondMember, HttpStatusCode::class)
}
}.build())
Expand Down Expand Up @@ -252,4 +258,15 @@ class ControllerVisitor(private val fileBuilder: FileSpec.Builder, private val l

private fun LerasiumCharter.documentationMemberName(methodName: String) =
MemberName(domain.toApiDocumentationClass(), methodName)

private fun CodeBlock.Builder.addContextCall() {
addStatement(
"val context = %M.%M<%T>()?.%M() ?: %T",
callMember,
principalMember,
JWTPrincipal::class,
toRequestContextMember,
AnonymousRequestContext::class
)
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package io.bkbn.lerasium.api.processor.visitor

import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.isAnnotationPresent
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSTypeReference
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
Expand All @@ -25,25 +21,19 @@ import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.ksp.addOriginatingKSFile
import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.toTypeName
import io.bkbn.lerasium.core.Domain
import io.bkbn.lerasium.core.Relation
import io.bkbn.lerasium.core.Sensitive
import io.bkbn.lerasium.core.converter.ConvertFrom
import io.bkbn.lerasium.core.model.IORequest
import io.bkbn.lerasium.core.model.IOResponse
import io.bkbn.lerasium.core.serialization.Serializers
import io.bkbn.lerasium.utils.KotlinPoetUtils.API_MODELS_PACKAGE_NAME
import io.bkbn.lerasium.utils.KotlinPoetUtils.addCodeBlock
import io.bkbn.lerasium.utils.KotlinPoetUtils.addObjectInstantiation
import io.bkbn.lerasium.utils.KotlinPoetUtils.isEnum
import io.bkbn.lerasium.utils.KotlinPoetUtils.isSupportedScalar
import io.bkbn.lerasium.utils.KotlinPoetUtils.collectProperties
import io.bkbn.lerasium.utils.KotlinPoetUtils.toParameter
import io.bkbn.lerasium.utils.KotlinPoetUtils.toProperty
import io.bkbn.lerasium.utils.LerasiumCharter
import io.bkbn.lerasium.utils.LerasiumUtils.getCollectionType
import io.bkbn.lerasium.utils.LerasiumUtils.getDomain
import io.bkbn.lerasium.utils.LerasiumUtils.isCollection
import io.bkbn.lerasium.utils.LerasiumUtils.isDomain
import io.bkbn.lerasium.utils.PropertyWrapper
import kotlinx.serialization.Serializable
import java.util.UUID

Expand Down Expand Up @@ -83,15 +73,15 @@ class RootModelVisitor(private val fileBuilder: FileSpec.Builder, private val lo
}

private fun TypeSpec.Builder.addCreateRequestModel(charter: LerasiumCharter) {
val props = charter.classDeclaration.collectProperties()
val props = charter.classDeclaration.collectProperties().filterId()
addType(TypeSpec.classBuilder("Create").apply {
addOriginatingKSFile(containingFile)
addModifiers(KModifier.DATA)
addAnnotation(AnnotationSpec.builder(Serializable::class).build())
addSuperinterface(IORequest.Create::class)
primaryConstructor(FunSpec.constructorBuilder().apply {
props.scalars.forEach { addParameter(it.toParameter()) }
props.domain.forEach {
props.relations.forEach {
val n = it.simpleName.getShortName()
addParameter(
ParameterSpec.builder(n, UUID::class).apply {
Expand All @@ -111,7 +101,7 @@ class RootModelVisitor(private val fileBuilder: FileSpec.Builder, private val lo
}.build())

props.scalars.forEach { addProperty(it.toProperty()) }
props.domain.forEach {
props.relations.forEach {
val n = it.simpleName.getShortName()
addProperty(PropertySpec.builder(n, UUID::class).apply {
initializer(n)
Expand Down Expand Up @@ -153,7 +143,7 @@ class RootModelVisitor(private val fileBuilder: FileSpec.Builder, private val lo
}.build()
)
}
props.domain.forEach {
props.relations.forEach {
addProperty(
PropertySpec.builder(it.simpleName.getShortName(), UUID::class.asClassName().copy(nullable = true))
.apply {
Expand All @@ -179,14 +169,14 @@ class RootModelVisitor(private val fileBuilder: FileSpec.Builder, private val lo
}.build())
}

private fun TypeSpec.Builder.updatePrimaryConstructor(properties: Properties) {
private fun TypeSpec.Builder.updatePrimaryConstructor(propWrapper: PropertyWrapper) {
primaryConstructor(FunSpec.constructorBuilder().apply {
properties.scalars.forEach {
propWrapper.scalars.forEach {
addParameter(
ParameterSpec.builder(it.simpleName.getShortName(), it.type.toTypeName().copy(nullable = true)).build()
)
}
properties.domain.forEach {
propWrapper.relations.forEach {
addParameter(
ParameterSpec.builder(it.simpleName.getShortName(), UUID::class.asClassName().copy(nullable = true))
.apply {
Expand All @@ -196,13 +186,13 @@ class RootModelVisitor(private val fileBuilder: FileSpec.Builder, private val lo
}.build()
)
}
properties.nested.forEach {
propWrapper.nested.forEach {
val n = it.simpleName.getShortName()
val t = it.type.resolve().toClassName().simpleName.plus(".Update")
val cn = ClassName(API_MODELS_PACKAGE_NAME, t).copy(nullable = true)
addParameter(ParameterSpec.builder(n, cn).build())
}
properties.enums.forEach {
propWrapper.enums.forEach {
addParameter(
ParameterSpec.builder(it.simpleName.getShortName(), it.type.toTypeName().copy(nullable = true)).build()
)
Expand All @@ -219,11 +209,9 @@ class RootModelVisitor(private val fileBuilder: FileSpec.Builder, private val lo
addSuperinterface(IOResponse::class.asTypeName())
responsePrimaryConstructor(props)
props.scalars.forEach { addProperty(it.toProperty()) }
props.domain.forEach {
props.relations.forEach {
val n = it.simpleName.getShortName()
val domain = (it.type.resolve().declaration as KSClassDeclaration).getAnnotationsByType(Domain::class).first()
val responseClass = ClassName(API_MODELS_PACKAGE_NAME, domain.name.plus("Models.Response"))
addProperty(PropertySpec.builder(n, responseClass).apply {
addProperty(PropertySpec.builder(n, UUID::class).apply {
initializer(n)
}.build())
}
Expand Down Expand Up @@ -262,38 +250,38 @@ class RootModelVisitor(private val fileBuilder: FileSpec.Builder, private val lo
}

private fun TypeSpec.Builder.responsePrimaryConstructor(
properties: Properties
propWrapper: PropertyWrapper
) {
primaryConstructor(FunSpec.constructorBuilder().apply {
properties.scalars.forEach { addParameter(it.toParameter()) }
properties.domain.forEach {
propWrapper.scalars.forEach { addParameter(it.toParameter()) }
propWrapper.relations.forEach {
val n = it.simpleName.getShortName()
val domain = (it.type.resolve().declaration as KSClassDeclaration).getAnnotationsByType(Domain::class).first()
val responseClass = ClassName(API_MODELS_PACKAGE_NAME, domain.name.plus("Models.Response"))
addParameter(ParameterSpec.builder(n, responseClass).build())
addParameter(ParameterSpec.builder(n, UUID::class).apply {
addAnnotation(AnnotationSpec.builder(Serializable::class).apply {
addMember("with = %T::class", Serializers.Uuid::class)
}.build())
}.build())
}
properties.nested.forEach {
propWrapper.nested.forEach {
val n = it.simpleName.getShortName()
val t = it.type.resolve().toClassName().simpleName.plus(".Response")
val cn = ClassName(API_MODELS_PACKAGE_NAME, t)
addParameter(ParameterSpec.builder(n, cn).build())
}
properties.enums.forEach {
propWrapper.enums.forEach {
addParameter(it.toParameter())
}
}.build())
}

private fun CodeBlock.Builder.addConverterProperties(properties: Properties) {
val filteredProps = properties.filterSensitive()
private fun CodeBlock.Builder.addConverterProperties(propWrapper: PropertyWrapper) {
val filteredProps = propWrapper.filterSensitive()
filteredProps.scalars.forEach {
addStatement("${it.simpleName.getShortName()} = input.${it.simpleName.getShortName()},")
}
filteredProps.domain.forEach {
filteredProps.relations.forEach {
val n = it.simpleName.getShortName()
val domain = (it.type.resolve().declaration as KSClassDeclaration).getAnnotationsByType(Domain::class).first()
val responseClass = ClassName(API_MODELS_PACKAGE_NAME, domain.name.plus("Models.Response"))
addStatement("$n = ${responseClass.simpleName}.from(input.${n}),")
addStatement("$n = input.$n.id,")
}
filteredProps.nested.forEach {
val n = it.simpleName.getShortName()
Expand All @@ -305,34 +293,4 @@ class RootModelVisitor(private val fileBuilder: FileSpec.Builder, private val lo
addStatement("${it.simpleName.getShortName()} = input.${it.simpleName.getShortName()},")
}
}

private data class Properties(
val scalars: Sequence<KSPropertyDeclaration>,
val domain: Sequence<KSPropertyDeclaration>,
val nested: Sequence<KSPropertyDeclaration>,
val enums: Sequence<KSPropertyDeclaration>,
) {
fun filterSensitive() = Properties(
scalars.filterNot { it.isAnnotationPresent(Sensitive::class) },
domain.filterNot { it.isAnnotationPresent(Sensitive::class) },
nested.filterNot { it.isAnnotationPresent(Sensitive::class) },
enums.filterNot { it.isAnnotationPresent(Sensitive::class) },
)
}

private fun KSClassDeclaration.collectProperties(): Properties {
val scalars = getAllProperties().filter { it.type.isSupportedScalar() }
val domain = getAllProperties().filter {
it.type.isDomain() || (it.type.isCollection() && it.type.getCollectionType().isDomain())
}
// TODO Cleaner way?
val nestedProps = getAllProperties()
.filterNot { it.type.isSupportedScalar() }
.filterNot { it.isAnnotationPresent(Relation::class) }
.filterNot { it.type.isDomain() }
.filterNot { it.type.isCollection() && it.type.getCollectionType().isDomain() }
.filterNot { it.type.isEnum() }
val enums = getAllProperties().filter { it.type.isEnum() }
return Properties(scalars, domain, nestedProps, enums)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.squareup.kotlinpoet.ksp.addOriginatingKSFile
import io.bkbn.lerasium.api.processor.Members.hmac256Member
import io.bkbn.lerasium.core.Relation
import io.bkbn.lerasium.core.model.LoginRequest
import io.bkbn.lerasium.core.request.RequestContext
import io.bkbn.lerasium.utils.KotlinPoetUtils.addCodeBlock
import io.bkbn.lerasium.utils.KotlinPoetUtils.addControlFlow
import io.bkbn.lerasium.utils.LerasiumCharter
Expand Down Expand Up @@ -53,10 +54,11 @@ class ServiceVisitor(private val fileBuilder: FileSpec.Builder, private val logg
private fun TypeSpec.Builder.addCreateFunction(charter: LerasiumCharter) {
addFunction(FunSpec.builder("create").apply {
addModifiers(KModifier.SUSPEND)
addParameter("context", RequestContext::class)
addParameter("request", charter.apiCreateRequestClass)
returns(charter.apiResponseClass)
addCodeBlock {
addStatement("val result = %T.create(request)", charter.repositoryClass)
addStatement("val result = %T.create(context, request)", charter.repositoryClass)
addStatement("return %T.from(result)", charter.apiResponseClass)
}
}.build())
Expand All @@ -65,10 +67,11 @@ class ServiceVisitor(private val fileBuilder: FileSpec.Builder, private val logg
private fun TypeSpec.Builder.addReadFunction(charter: LerasiumCharter) {
addFunction(FunSpec.builder("read").apply {
addModifiers(KModifier.SUSPEND)
addParameter("context", RequestContext::class)
addParameter("id", UUID::class)
returns(charter.apiResponseClass)
addCodeBlock {
addStatement("val result = %T.read(id)", charter.repositoryClass)
addStatement("val result = %T.read(context, id)", charter.repositoryClass)
addStatement("return %T.from(result)", charter.apiResponseClass)
}
}.build())
Expand All @@ -77,11 +80,12 @@ class ServiceVisitor(private val fileBuilder: FileSpec.Builder, private val logg
private fun TypeSpec.Builder.addUpdateFunction(charter: LerasiumCharter) {
addFunction(FunSpec.builder("update").apply {
addModifiers(KModifier.SUSPEND)
addParameter("context", RequestContext::class)
addParameter("id", UUID::class)
addParameter("request", charter.apiUpdateRequestClass)
returns(charter.apiResponseClass)
addCodeBlock {
addStatement("val result = %T.update(id, request)", charter.repositoryClass)
addStatement("val result = %T.update(context, id, request)", charter.repositoryClass)
addStatement("return %T.from(result)", charter.apiResponseClass)
}
}.build())
Expand All @@ -90,9 +94,10 @@ class ServiceVisitor(private val fileBuilder: FileSpec.Builder, private val logg
private fun TypeSpec.Builder.addDeleteFunction(charter: LerasiumCharter) {
addFunction(FunSpec.builder("delete").apply {
addModifiers(KModifier.SUSPEND)
addParameter("context", RequestContext::class)
addParameter("id", UUID::class)
addCodeBlock {
addStatement("%T.delete(id)", charter.repositoryClass)
addStatement("%T.delete(context, id)", charter.repositoryClass)
}
}.build())
}
Expand Down
Loading

0 comments on commit e3c49df

Please sign in to comment.