mirror of https://github.com/usbharu/Hideout.git
Merge pull request #44 from usbharu/feature/delete-ktor
Feature/delete ktor
This commit is contained in:
commit
34b19829f4
107
build.gradle.kts
107
build.gradle.kts
|
@ -1,4 +1,3 @@
|
|||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
|
||||
|
||||
|
@ -11,7 +10,6 @@ val koin_version: String by project
|
|||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.8.21"
|
||||
id("io.ktor.plugin") version "2.3.0"
|
||||
id("org.graalvm.buildtools.native") version "0.9.21"
|
||||
id("io.gitlab.arturbosch.detekt") version "1.23.1"
|
||||
id("com.google.devtools.ksp") version "1.8.21-1.0.11"
|
||||
|
@ -27,12 +25,6 @@ apply {
|
|||
|
||||
group = "dev.usbharu"
|
||||
version = "0.0.1"
|
||||
application {
|
||||
mainClass.set("dev.usbharu.hideout.SpringApplicationKt")
|
||||
|
||||
val isDevelopment: Boolean = project.ext.has("development")
|
||||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
|
@ -51,47 +43,11 @@ tasks.withType<KotlinCompile> {
|
|||
mustRunAfter("openApiGenerateMastodonCompatibleApi")
|
||||
}
|
||||
|
||||
tasks.withType<ShadowJar> {
|
||||
manifest {
|
||||
attributes(
|
||||
"Implementation-Version" to project.version.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.clean {
|
||||
delete += listOf("$rootDir/src/main/resources/static")
|
||||
}
|
||||
|
||||
//tasks.create<GenerateTask>("openApiGenerateServer", GenerateTask::class) {
|
||||
// generatorName.set("kotlin-spring")
|
||||
// inputSpec.set("$rootDir/src/main/resources/openapi/api.yaml")
|
||||
// outputDir.set("$buildDir/generated/sources/openapi")
|
||||
// apiPackage.set("dev.usbharu.hideout.controller.generated")
|
||||
// modelPackage.set("dev.usbharu.hideout.domain.model.generated")
|
||||
// configOptions.put("interfaceOnly", "true")
|
||||
// configOptions.put("useSpringBoot3", "true")
|
||||
// additionalProperties.put("useTags", "true")
|
||||
// schemaMappings.putAll(
|
||||
// mapOf(
|
||||
// "ReactionResponse" to "dev.usbharu.hideout.domain.model.hideout.dto.ReactionResponse",
|
||||
// "Account" to "dev.usbharu.hideout.domain.model.hideout.dto.Account",
|
||||
// "JwtToken" to "dev.usbharu.hideout.domain.model.hideout.dto.JwtToken",
|
||||
// "PostRequest" to "dev.usbharu.hideout.domain.model.hideout.form.Post",
|
||||
// "PostResponse" to "dev.usbharu.hideout.domain.model.hideout.dto.PostResponse",
|
||||
// "Reaction" to "dev.usbharu.hideout.domain.model.hideout.form.Reaction",
|
||||
// "RefreshToken" to "dev.usbharu.hideout.domain.model.hideout.form.RefreshToken",
|
||||
// "UserLogin" to "dev.usbharu.hideout.domain.model.hideout.form.UserLogin",
|
||||
// "UserResponse" to "dev.usbharu.hideout.domain.model.hideout.dto.UserResponse",
|
||||
// "UserCreate" to "dev.usbharu.hideout.domain.model.hideout.form.UserCreate",
|
||||
// "Visibility" to "dev.usbharu.hideout.domain.model.hideout.entity.Visibility",
|
||||
// )
|
||||
// )
|
||||
//
|
||||
//// importMappings.putAll(mapOf("ReactionResponse" to "ReactionResponse"))
|
||||
//// typeMappings.putAll(mapOf("ReactionResponse" to "ReactionResponse"))
|
||||
//}
|
||||
|
||||
tasks.create<GenerateTask>("openApiGenerateMastodonCompatibleApi", GenerateTask::class) {
|
||||
generatorName.set("kotlin-spring")
|
||||
inputSpec.set("$rootDir/src/main/resources/openapi/mastodon.yaml")
|
||||
|
@ -128,31 +84,21 @@ sourceSets.main {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-auth:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-auth-jwt:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-sessions-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-auto-head-response-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-cors-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-default-headers-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-forwarded-header-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-call-logging-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-serialization-jackson:$ktor_version")
|
||||
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
|
||||
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
|
||||
implementation("com.h2database:h2:$h2_version")
|
||||
implementation("org.xerial:sqlite-jdbc:3.40.1.0")
|
||||
implementation("io.ktor:ktor-server-websockets-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-cio-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-compression:$ktor_version")
|
||||
implementation("ch.qos.logback:logback-classic:$logback_version")
|
||||
implementation("com.auth0:java-jwt:4.4.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
|
||||
|
||||
implementation("io.insert-koin:koin-core:$koin_version")
|
||||
implementation("io.insert-koin:koin-ktor:$koin_version")
|
||||
implementation("io.insert-koin:koin-logger-slf4j:$koin_version")
|
||||
implementation("io.insert-koin:koin-annotations:1.2.0")
|
||||
implementation("io.ktor:ktor-server-compression-jvm:2.3.0")
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
ksp("io.insert-koin:koin-ksp-compiler:1.2.0")
|
||||
|
||||
|
@ -178,10 +124,7 @@ dependencies {
|
|||
implementation("org.springframework.security:spring-security-oauth2-jose")
|
||||
|
||||
implementation("io.ktor:ktor-client-logging-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-host-common-jvm:$ktor_version")
|
||||
implementation("io.ktor:ktor-server-status-pages-jvm:$ktor_version")
|
||||
|
||||
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
|
||||
|
||||
|
@ -197,53 +140,11 @@ dependencies {
|
|||
|
||||
|
||||
implementation("org.drewcarlson:kjob-core:0.6.0")
|
||||
testImplementation("io.ktor:ktor-server-test-host-jvm:$ktor_version")
|
||||
|
||||
testImplementation("org.slf4j:slf4j-simple:2.0.7")
|
||||
|
||||
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.22.0")
|
||||
}
|
||||
|
||||
jib {
|
||||
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||
dockerClient.environment = mapOf(
|
||||
"DOCKER_HOST" to "localhost:2375"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ktor {
|
||||
docker {
|
||||
localImageName.set("hideout")
|
||||
}
|
||||
}
|
||||
|
||||
graalvmNative {
|
||||
binaries {
|
||||
named("main") {
|
||||
fallback.set(false)
|
||||
verbose.set(true)
|
||||
agent {
|
||||
enabled.set(false)
|
||||
}
|
||||
|
||||
buildArgs.add("--initialize-at-build-time=io.ktor,kotlin,kotlinx")
|
||||
// buildArgs.add("--trace-class-initialization=ch.qos.logback.classic.Logger")
|
||||
// buildArgs.add("--trace-object-instantiation=ch.qos.logback.core.AsyncAppenderBase"+"$"+"Worker")
|
||||
// buildArgs.add("--trace-object-instantiation=ch.qos.logback.classic.Logger")
|
||||
buildArgs.add("--initialize-at-build-time=org.slf4j.LoggerFactory,ch.qos.logback")
|
||||
// buildArgs.add("--trace-object-instantiation=kotlinx.coroutines.channels.ArrayChannel")
|
||||
buildArgs.add("--initialize-at-build-time=kotlinx.coroutines.channels.ArrayChannel")
|
||||
buildArgs.add("-H:+InstallExitHandlers")
|
||||
buildArgs.add("-H:+ReportUnsupportedElementsAtRuntime")
|
||||
buildArgs.add("-H:+ReportExceptionStackTraces")
|
||||
|
||||
runtimeArgs.add("-config=$buildDir/resources/main/application-native.conf")
|
||||
imageName.set("graal-server")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
detekt {
|
||||
parallel = true
|
||||
config = files("detekt.yml")
|
||||
|
|
|
@ -1,172 +0,0 @@
|
|||
package dev.usbharu.hideout
|
||||
|
||||
import com.auth0.jwk.JwkProvider
|
||||
import com.auth0.jwk.JwkProviderBuilder
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import dev.usbharu.hideout.config.CharacterLimit
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.config.ConfigData
|
||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
||||
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
|
||||
import dev.usbharu.hideout.domain.model.job.DeliverRemoveReactionJob
|
||||
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
||||
import dev.usbharu.hideout.plugins.*
|
||||
import dev.usbharu.hideout.query.FollowerQueryService
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.routing.register
|
||||
import dev.usbharu.hideout.service.ap.APService
|
||||
import dev.usbharu.hideout.service.ap.APUserService
|
||||
import dev.usbharu.hideout.service.api.PostApiService
|
||||
import dev.usbharu.hideout.service.api.UserApiService
|
||||
import dev.usbharu.hideout.service.api.UserAuthApiService
|
||||
import dev.usbharu.hideout.service.api.WebFingerApiService
|
||||
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
|
||||
import dev.usbharu.hideout.service.core.*
|
||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||
import dev.usbharu.hideout.service.job.KJobJobQueueParentService
|
||||
import dev.usbharu.hideout.service.user.UserService
|
||||
import dev.usbharu.kjob.exposed.ExposedKJob
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.cio.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.server.application.*
|
||||
import kjob.core.kjob
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.koin.ksp.generated.module
|
||||
import org.koin.ktor.ext.inject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun main(args: Array<String>): Unit = io.ktor.server.cio.EngineMain.main(args)
|
||||
|
||||
val Application.property: Application.(propertyName: String) -> String
|
||||
get() = {
|
||||
environment.config.property(it).getString()
|
||||
}
|
||||
|
||||
val Application.propertyOrNull: Application.(propertyName: String) -> String?
|
||||
get() = {
|
||||
environment.config.propertyOrNull(it)?.getString()
|
||||
}
|
||||
|
||||
// application.conf references the main function. This annotation prevents the IDE from marking it as unused.
|
||||
@Deprecated("Ktor is deprecated")
|
||||
@Suppress("unused", "LongMethod")
|
||||
fun Application.parent() {
|
||||
Config.configData = ConfigData(
|
||||
url = property("hideout.url"),
|
||||
objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false),
|
||||
characterLimit = CharacterLimit(
|
||||
general = CharacterLimit.General.of(
|
||||
url = propertyOrNull("hideout.character-limit.general.url")?.toIntOrNull(),
|
||||
domain = propertyOrNull("hideout.character-limit.general.domain")?.toIntOrNull(),
|
||||
publicKey = propertyOrNull("hideout.character-limit.general.publicKey")?.toIntOrNull(),
|
||||
privateKey = propertyOrNull("hideout.character-limit.general.privateKey")?.toIntOrNull()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val module = org.koin.dsl.module {
|
||||
single<Database> {
|
||||
Database.connect(
|
||||
url = property("hideout.database.url"),
|
||||
driver = property("hideout.database.driver"),
|
||||
user = property("hideout.database.username"),
|
||||
password = property("hideout.database.password")
|
||||
)
|
||||
}
|
||||
single<JobQueueParentService> {
|
||||
val kJobJobQueueService = KJobJobQueueParentService(get())
|
||||
kJobJobQueueService.init(emptyList())
|
||||
kJobJobQueueService
|
||||
}
|
||||
single<HttpClient> {
|
||||
HttpClient(CIO).config {
|
||||
install(Logging) {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.INFO
|
||||
}
|
||||
install(httpSignaturePlugin) {
|
||||
keyMap = KtorKeyMap(get(), get())
|
||||
}
|
||||
expectSuccess = true
|
||||
}
|
||||
}
|
||||
single<IdGenerateService> { TwitterSnowflakeIdGenerateService }
|
||||
single<JwkProvider> {
|
||||
JwkProviderBuilder(Config.configData.url).cached(
|
||||
10,
|
||||
24,
|
||||
TimeUnit.HOURS
|
||||
)
|
||||
.rateLimited(10, 1, TimeUnit.MINUTES).build()
|
||||
}
|
||||
}
|
||||
configureKoin(module, HideoutModule().module)
|
||||
configureStatusPages()
|
||||
runBlocking {
|
||||
inject<ServerInitialiseService>().value.init()
|
||||
}
|
||||
configureCompression()
|
||||
configureHTTP()
|
||||
configureStaticRouting()
|
||||
configureMonitoring()
|
||||
configureSerialization()
|
||||
register(inject<UserApiService>().value)
|
||||
configureSecurity(
|
||||
|
||||
inject<JwkProvider>().value,
|
||||
inject<MetaService>().value
|
||||
)
|
||||
configureRouting(
|
||||
httpSignatureVerifyService = inject<HttpSignatureVerifyService>().value,
|
||||
apService = inject<APService>().value,
|
||||
userService = inject<UserService>().value,
|
||||
apUserService = inject<APUserService>().value,
|
||||
postService = inject<PostApiService>().value,
|
||||
userApiService = inject<UserApiService>().value,
|
||||
userQueryService = inject<UserQueryService>().value,
|
||||
followerQueryService = inject<FollowerQueryService>().value,
|
||||
userAuthApiService = inject<UserAuthApiService>().value,
|
||||
webFingerApiService = inject<WebFingerApiService>().value,
|
||||
transaction = inject<Transaction>().value
|
||||
)
|
||||
}
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
@Suppress("unused")
|
||||
fun Application.worker() {
|
||||
val kJob = kjob(ExposedKJob) {
|
||||
connectionDatabase = inject<Database>().value
|
||||
}.start()
|
||||
|
||||
val apService = inject<APService>().value
|
||||
|
||||
kJob.register(ReceiveFollowJob) {
|
||||
execute {
|
||||
apService.processActivity(this, it)
|
||||
}
|
||||
}
|
||||
kJob.register(DeliverPostJob) {
|
||||
execute {
|
||||
apService.processActivity(this, it)
|
||||
}
|
||||
}
|
||||
|
||||
kJob.register(DeliverReactionJob) {
|
||||
execute {
|
||||
apService.processActivity(this, it)
|
||||
}
|
||||
}
|
||||
|
||||
kJob.register(DeliverRemoveReactionJob) {
|
||||
execute {
|
||||
apService.processActivity(this, it)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,6 @@ import java.security.interfaces.RSAPrivateKey
|
|||
import java.security.interfaces.RSAPublicKey
|
||||
import java.util.*
|
||||
|
||||
|
||||
@EnableWebSecurity(debug = true)
|
||||
@Configuration
|
||||
class SecurityConfig {
|
||||
|
@ -152,8 +151,6 @@ class SecurityConfig {
|
|||
if (OAuth2TokenType.ACCESS_TOKEN == context.tokenType) {
|
||||
val userDetailsImpl = context.getPrincipal<Authentication>().principal as UserDetailsImpl
|
||||
context.claims.claim("uid", userDetailsImpl.id.toString())
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,5 +35,4 @@ class MastodonAppsApiController(private val appApiService: AppApiService) : AppA
|
|||
HttpStatus.OK
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,8 +30,6 @@ class UserDetailsImpl(
|
|||
@Serial
|
||||
private const val serialVersionUID: Long = -899168205656607781L
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
|
@ -46,11 +44,9 @@ class UserDetailsImpl(
|
|||
@JsonSubTypes
|
||||
abstract class UserDetailsMixin
|
||||
|
||||
|
||||
class UserDetailsDeserializer : JsonDeserializer<UserDetailsImpl>() {
|
||||
val SIMPLE_GRANTED_AUTHORITY_SET = object : TypeReference<Set<SimpleGrantedAuthority>>() {}
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UserDetailsImpl {
|
||||
|
||||
val mapper = p.codec as ObjectMapper
|
||||
val jsonNode: JsonNode = mapper.readTree(p)
|
||||
println(jsonNode)
|
||||
|
@ -70,7 +66,6 @@ class UserDetailsDeserializer : JsonDeserializer<UserDetailsImpl>() {
|
|||
true,
|
||||
authorities.toMutableList(),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
fun JsonNode.readText(field: String, defaultValue: String = ""): String {
|
||||
|
@ -79,5 +74,4 @@ class UserDetailsDeserializer : JsonDeserializer<UserDetailsImpl>() {
|
|||
else -> defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@ import io.ktor.client.plugins.api.*
|
|||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import tech.barbero.http.message.signing.HttpMessage
|
||||
import tech.barbero.http.message.signing.HttpMessageSigner
|
||||
|
@ -28,12 +26,6 @@ import java.text.SimpleDateFormat
|
|||
import java.util.*
|
||||
import javax.crypto.SecretKey
|
||||
|
||||
suspend fun <T : JsonLd> ApplicationCall.respondAp(message: T, status: HttpStatusCode = HttpStatusCode.OK) {
|
||||
message.context += "https://www.w3.org/ns/activitystreams"
|
||||
val activityJson = Config.configData.objectMapper.writeValueAsString(message)
|
||||
respondText(activityJson, ContentType.Application.Activity, status)
|
||||
}
|
||||
|
||||
suspend fun HttpClient.postAp(urlString: String, username: String, jsonLd: JsonLd): HttpResponse {
|
||||
jsonLd.context += "https://www.w3.org/ns/activitystreams"
|
||||
return this.post(urlString) {
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.compression.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Application.configureCompression() {
|
||||
install(Compression) {
|
||||
gzip {
|
||||
matchContentType(ContentType.Application.JavaScript)
|
||||
priority = 1.0
|
||||
}
|
||||
deflate {
|
||||
matchContentType(ContentType.Application.JavaScript)
|
||||
priority = 10.0
|
||||
minimumSize(1024) // condition
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.defaultheaders.*
|
||||
import io.ktor.server.plugins.forwardedheaders.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Application.configureHTTP() {
|
||||
// install(CORS) {
|
||||
// allowMethod(HttpMethod.Options)
|
||||
// allowMethod(HttpMethod.Put)
|
||||
// allowMethod(HttpMethod.Delete)
|
||||
// allowMethod(HttpMethod.Patch)
|
||||
// allowHeader(HttpHeaders.Authorization)
|
||||
// allow
|
||||
// allowHeader("MyCustomHeader")
|
||||
// anyHost() // @TODO: Don't do this in production if possible. Try to limit it.
|
||||
// }
|
||||
install(DefaultHeaders) {
|
||||
header("X-Engine", "Ktor") // will send this header with each response
|
||||
}
|
||||
install(ForwardedHeaders) // WARNING: for security, do not include this if not behind a reverse proxy
|
||||
install(XForwardedHeaders) // WARNING: for security, do not include this if not behind a reverse proxy
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.ktor.plugin.Koin
|
||||
import org.koin.logger.slf4jLogger
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Application.configureKoin(vararg module: Module) {
|
||||
install(Koin) {
|
||||
slf4jLogger()
|
||||
modules(*module)
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.callloging.*
|
||||
import org.slf4j.event.Level
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Application.configureMonitoring() {
|
||||
install(CallLogging) {
|
||||
level = Level.INFO
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import dev.usbharu.hideout.query.FollowerQueryService
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.routing.activitypub.inbox
|
||||
import dev.usbharu.hideout.routing.activitypub.outbox
|
||||
import dev.usbharu.hideout.routing.activitypub.usersAP
|
||||
import dev.usbharu.hideout.routing.api.internal.v1.auth
|
||||
import dev.usbharu.hideout.routing.api.internal.v1.posts
|
||||
import dev.usbharu.hideout.routing.api.internal.v1.users
|
||||
import dev.usbharu.hideout.routing.wellknown.webfinger
|
||||
import dev.usbharu.hideout.service.ap.APService
|
||||
import dev.usbharu.hideout.service.ap.APUserService
|
||||
import dev.usbharu.hideout.service.api.PostApiService
|
||||
import dev.usbharu.hideout.service.api.UserApiService
|
||||
import dev.usbharu.hideout.service.api.UserAuthApiService
|
||||
import dev.usbharu.hideout.service.api.WebFingerApiService
|
||||
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.user.UserService
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.autohead.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
@Suppress("LongParameterList")
|
||||
fun Application.configureRouting(
|
||||
httpSignatureVerifyService: HttpSignatureVerifyService,
|
||||
apService: APService,
|
||||
userService: UserService,
|
||||
apUserService: APUserService,
|
||||
postService: PostApiService,
|
||||
userApiService: UserApiService,
|
||||
userQueryService: UserQueryService,
|
||||
followerQueryService: FollowerQueryService,
|
||||
userAuthApiService: UserAuthApiService,
|
||||
webFingerApiService: WebFingerApiService,
|
||||
transaction: Transaction
|
||||
) {
|
||||
install(AutoHeadResponse)
|
||||
routing {
|
||||
inbox(httpSignatureVerifyService, apService)
|
||||
outbox()
|
||||
usersAP(apUserService, userQueryService, followerQueryService, transaction)
|
||||
webfinger(webFingerApiService)
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
users(userService, userApiService)
|
||||
auth(userAuthApiService)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import com.auth0.jwk.JwkProvider
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.service.core.MetaService
|
||||
import dev.usbharu.hideout.util.JsonWebKeyUtil
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
const val TOKEN_AUTH = "jwt-auth"
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
@Suppress("MagicNumber")
|
||||
fun Application.configureSecurity(
|
||||
jwkProvider: JwkProvider,
|
||||
metaService: MetaService
|
||||
) {
|
||||
val issuer = Config.configData.url
|
||||
install(Authentication) {
|
||||
jwt(TOKEN_AUTH) {
|
||||
verifier(jwkProvider, issuer) {
|
||||
acceptLeeway(3)
|
||||
}
|
||||
validate { jwtCredential ->
|
||||
val uid = jwtCredential.payload.getClaim("uid")
|
||||
if (uid.isMissing) {
|
||||
return@validate null
|
||||
}
|
||||
if (uid.asLong() == null) {
|
||||
return@validate null
|
||||
}
|
||||
return@validate JWTPrincipal(jwtCredential.payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
routing {
|
||||
get("/.well-known/jwks.json") {
|
||||
//language=JSON
|
||||
val jwt = metaService.getJwtMeta()
|
||||
call.respondText(
|
||||
contentType = ContentType.Application.Json,
|
||||
text = JsonWebKeyUtil.publicKeyToJwk(jwt.publicKey, jwt.kid.toString())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.annotation.JsonSetter
|
||||
import com.fasterxml.jackson.annotation.Nulls
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import io.ktor.serialization.jackson.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Application.configureSerialization() {
|
||||
install(ContentNegotiation) {
|
||||
jackson {
|
||||
enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
configOverride(List::class.java).setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.http.content.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Application.configureStaticRouting() {
|
||||
routing {
|
||||
get("/") {
|
||||
call.respondText(
|
||||
String.javaClass.classLoader.getResourceAsStream("static/index.html").readAllBytes().decodeToString(),
|
||||
contentType = ContentType.Text.Html
|
||||
)
|
||||
}
|
||||
static("/") {
|
||||
resources("static")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import dev.usbharu.hideout.exception.InvalidUsernameOrPasswordException
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.statuspages.*
|
||||
import io.ktor.server.response.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Application.configureStatusPages() {
|
||||
install(StatusPages) {
|
||||
exception<IllegalArgumentException> { call, cause ->
|
||||
call.respondText(text = "400: $cause", status = HttpStatusCode.BadRequest)
|
||||
call.application.log.warn("Bad Request", cause)
|
||||
}
|
||||
exception<InvalidUsernameOrPasswordException> { call, _ ->
|
||||
call.respond(HttpStatusCode.Unauthorized)
|
||||
}
|
||||
exception<Throwable> { call, cause ->
|
||||
call.respondText(text = "500: ${cause.stackTraceToString()}", status = HttpStatusCode.InternalServerError)
|
||||
call.application.log.error("Internal Server Error", cause)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -175,7 +175,6 @@ class RegisteredClientRepositoryImpl(private val database: Database) : Registere
|
|||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package dev.usbharu.hideout.routing
|
||||
|
||||
import dev.usbharu.hideout.service.api.UserApiService
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Application.register(userApiService: UserApiService) {
|
||||
routing {
|
||||
get("/register") {
|
||||
val principal = call.principal<UserIdPrincipal>()
|
||||
if (principal != null) {
|
||||
call.respondRedirect("/users/${principal.name}")
|
||||
}
|
||||
call.respondText(ContentType.Text.Html) {
|
||||
//language=HTML
|
||||
"""
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<form method='post' action=''>
|
||||
<input type='text' name='username' value=''>
|
||||
<input type='password' name='password'>
|
||||
<input type="submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
post("/register") {
|
||||
val parameters = call.receiveParameters()
|
||||
val password = parameters["password"] ?: return@post call.respondRedirect("/register")
|
||||
val username = parameters["username"] ?: return@post call.respondRedirect("/register")
|
||||
userApiService.createUser(username, password)
|
||||
call.respondRedirect("/users/$username")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.activitypub
|
||||
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.ActivityPubObjectResponse
|
||||
import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
|
||||
import dev.usbharu.hideout.exception.HttpSignatureVerifyException
|
||||
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Routing.inbox(
|
||||
httpSignatureVerifyService: HttpSignatureVerifyService,
|
||||
apService: dev.usbharu.hideout.service.ap.APService
|
||||
) {
|
||||
route("/inbox") {
|
||||
get {
|
||||
call.respond(HttpStatusCode.MethodNotAllowed)
|
||||
}
|
||||
post {
|
||||
if (httpSignatureVerifyService.verify(call.request.headers).not()) {
|
||||
throw HttpSignatureVerifyException()
|
||||
}
|
||||
val json = call.receiveText()
|
||||
call.application.log.trace("Received: $json")
|
||||
val activityTypes = apService.parseActivity(json)
|
||||
call.application.log.debug("ActivityTypes: ${activityTypes.name}")
|
||||
val response = apService.processActivity(json, activityTypes)
|
||||
when (response) {
|
||||
is ActivityPubObjectResponse -> call.respond(
|
||||
response.httpStatusCode,
|
||||
Config.configData.objectMapper.writeValueAsString(
|
||||
response.message.apply {
|
||||
context =
|
||||
listOf("https://www.w3.org/ns/activitystreams")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
is ActivityPubStringResponse -> call.respond(response.httpStatusCode, response.message)
|
||||
null -> call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
route("/users/{name}/inbox") {
|
||||
get {
|
||||
call.respond(HttpStatusCode.MethodNotAllowed)
|
||||
}
|
||||
post {
|
||||
if (httpSignatureVerifyService.verify(call.request.headers).not()) {
|
||||
throw HttpSignatureVerifyException()
|
||||
}
|
||||
val json = call.receiveText()
|
||||
call.application.log.trace("Received: $json")
|
||||
val activityTypes = apService.parseActivity(json)
|
||||
call.application.log.debug("ActivityTypes: ${activityTypes.name}")
|
||||
val response = apService.processActivity(json, activityTypes)
|
||||
when (response) {
|
||||
is ActivityPubObjectResponse -> call.respond(
|
||||
response.httpStatusCode,
|
||||
Config.configData.objectMapper.writeValueAsString(
|
||||
response.message.apply {
|
||||
context =
|
||||
listOf("https://www.w3.org/ns/activitystreams")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
is ActivityPubStringResponse -> call.respond(response.httpStatusCode, response.message)
|
||||
null -> call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.activitypub
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Routing.outbox() {
|
||||
route("/outbox") {
|
||||
get {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
post {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
route("/users/{name}/outbox") {
|
||||
get {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
post {
|
||||
call.respond(HttpStatusCode.NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.activitypub
|
||||
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||
import dev.usbharu.hideout.plugins.respondAp
|
||||
import dev.usbharu.hideout.query.FollowerQueryService
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.ap.APUserService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||
import dev.usbharu.hideout.util.HttpUtil.JsonLd
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Routing.usersAP(
|
||||
apUserService: APUserService,
|
||||
userQueryService: UserQueryService,
|
||||
followerQueryService: FollowerQueryService,
|
||||
transaction: Transaction
|
||||
) {
|
||||
route("/users/{name}") {
|
||||
createChild(ContentTypeRouteSelector(ContentType.Application.Activity, ContentType.Application.JsonLd)).handle {
|
||||
call.application.log.debug("Signature: ${call.request.header("Signature")}")
|
||||
call.application.log.debug("Authorization: ${call.request.header("Authorization")}")
|
||||
val name =
|
||||
call.parameters["name"] ?: throw ParameterNotExistException("Parameter(name='name') does not exist.")
|
||||
val person = apUserService.getPersonByName(name)
|
||||
return@handle call.respondAp(person, HttpStatusCode.OK)
|
||||
}
|
||||
get {
|
||||
// TODO: 暫定処置なので治す
|
||||
transaction.transaction {
|
||||
val userEntity = userQueryService.findByNameAndDomain(
|
||||
call.parameters["name"]
|
||||
?: throw ParameterNotExistException("Parameter(name='name') does not exist."),
|
||||
Config.configData.domain
|
||||
)
|
||||
val personByName = apUserService.getPersonByName(userEntity.name)
|
||||
call.respondText(
|
||||
userEntity.toString() + "\n" + followerQueryService.findFollowersById(userEntity.id) +
|
||||
"\n" + Config.configData.objectMapper.writeValueAsString(
|
||||
personByName
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
class ContentTypeRouteSelector(private vararg val contentType: ContentType) : RouteSelector() {
|
||||
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {
|
||||
context.call.application.log.debug("Accept: ${context.call.request.accept()}")
|
||||
val requestContentType = context.call.request.accept() ?: return RouteSelectorEvaluation.FailedParameter
|
||||
return if (requestContentType.split(",").any { contentType.any { contentType -> contentType.match(it) } }) {
|
||||
RouteSelectorEvaluation.Constant
|
||||
} else {
|
||||
RouteSelectorEvaluation.FailedParameter
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.api.internal.v1
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
|
||||
import dev.usbharu.hideout.plugins.TOKEN_AUTH
|
||||
import dev.usbharu.hideout.service.api.UserAuthApiService
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Route.auth(userAuthApiService: UserAuthApiService) {
|
||||
post("/login") {
|
||||
val loginUser = call.receive<UserLogin>()
|
||||
return@post call.respond(userAuthApiService.login(loginUser.username, loginUser.password))
|
||||
}
|
||||
|
||||
post("/refresh-token") {
|
||||
val refreshToken = call.receive<RefreshToken>()
|
||||
return@post call.respond(userAuthApiService.refreshToken(refreshToken))
|
||||
}
|
||||
authenticate(TOKEN_AUTH) {
|
||||
get("/auth-check") {
|
||||
val principal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
|
||||
val username = principal.payload.getClaim("uid")
|
||||
call.respondText("Hello $username")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.api.internal.v1
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.Post
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.Reaction
|
||||
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||
import dev.usbharu.hideout.plugins.TOKEN_AUTH
|
||||
import dev.usbharu.hideout.service.api.PostApiService
|
||||
import dev.usbharu.hideout.util.InstantParseUtil
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
@Suppress("LongMethod")
|
||||
fun Route.posts(postApiService: PostApiService) {
|
||||
route("/posts") {
|
||||
authenticate(TOKEN_AUTH) {
|
||||
post {
|
||||
val principal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
|
||||
val userId = principal.payload.getClaim("uid").asLong()
|
||||
|
||||
val receive = call.receive<Post>()
|
||||
val create = postApiService.createPost(receive, userId)
|
||||
call.response.header("Location", create.url)
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
route("/{id}/reactions") {
|
||||
get {
|
||||
val principal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
|
||||
val userId = principal.payload.getClaim("uid").asLong()
|
||||
val postId = (
|
||||
call.parameters["id"]?.toLong()
|
||||
?: throw ParameterNotExistException("Parameter(id='postsId') does not exist.")
|
||||
)
|
||||
call.respond(postApiService.getReactionByPostId(postId, userId))
|
||||
}
|
||||
post {
|
||||
val jwtPrincipal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
|
||||
val userId = jwtPrincipal.payload.getClaim("uid").asLong()
|
||||
val postId = call.parameters["id"]?.toLong()
|
||||
?: throw ParameterNotExistException("Parameter(id='postsId') does not exist.")
|
||||
val reaction = try {
|
||||
call.receive<Reaction>()
|
||||
} catch (_: ContentTransformationException) {
|
||||
Reaction(null)
|
||||
}
|
||||
|
||||
postApiService.appendReaction(reaction.reaction ?: "❤", userId, postId)
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
}
|
||||
delete {
|
||||
val jwtPrincipal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
|
||||
val userId = jwtPrincipal.payload.getClaim("uid").asLong()
|
||||
val postId = call.parameters["id"]?.toLong()
|
||||
?: throw ParameterNotExistException("Parameter(id='postsId') does not exist.")
|
||||
postApiService.removeReaction(userId, postId)
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
authenticate(TOKEN_AUTH, optional = true) {
|
||||
get {
|
||||
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
|
||||
val since = InstantParseUtil.parse(call.request.queryParameters["since"])
|
||||
val until = InstantParseUtil.parse(call.request.queryParameters["until"])
|
||||
val minId = call.request.queryParameters["minId"]?.toLong()
|
||||
val maxId = call.request.queryParameters["maxId"]?.toLong()
|
||||
val limit = call.request.queryParameters["limit"]?.toInt()
|
||||
call.respond(HttpStatusCode.OK, postApiService.getAll(since, until, minId, maxId, limit, userId))
|
||||
}
|
||||
get("/{id}") {
|
||||
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
|
||||
val id = call.parameters["id"]?.toLong()
|
||||
?: throw ParameterNotExistException("Parameter(id='postsId') does not exist.")
|
||||
val post = postApiService.getById(id, userId)
|
||||
call.respond(post)
|
||||
}
|
||||
}
|
||||
}
|
||||
route("/users/{name}/posts") {
|
||||
authenticate(TOKEN_AUTH, optional = true) {
|
||||
get {
|
||||
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
|
||||
val targetUserName = call.parameters["name"]
|
||||
?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.")
|
||||
val posts = postApiService.getByUser(targetUserName, userId = userId)
|
||||
call.respond(posts)
|
||||
}
|
||||
get("/{id}") {
|
||||
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
|
||||
val id = call.parameters["id"]?.toLong()
|
||||
?: throw ParameterNotExistException("Parameter(name='postsId' does not exist.")
|
||||
val post = postApiService.getById(id, userId)
|
||||
call.respond(post)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.api.internal.v1
|
||||
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.UserCreate
|
||||
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||
import dev.usbharu.hideout.plugins.TOKEN_AUTH
|
||||
import dev.usbharu.hideout.service.api.UserApiService
|
||||
import dev.usbharu.hideout.service.user.UserService
|
||||
import dev.usbharu.hideout.util.AcctUtil
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
@Suppress("LongMethod", "CognitiveComplexMethod")
|
||||
fun Route.users(userService: UserService, userApiService: UserApiService) {
|
||||
route("/users") {
|
||||
get {
|
||||
call.respond(userApiService.findAll())
|
||||
}
|
||||
post {
|
||||
val userCreate = call.receive<UserCreate>()
|
||||
if (userService.usernameAlreadyUse(userCreate.username)) {
|
||||
return@post call.respond(HttpStatusCode.BadRequest)
|
||||
}
|
||||
val user = userService.createLocalUser(
|
||||
UserCreateDto(
|
||||
userCreate.username,
|
||||
userCreate.username,
|
||||
"",
|
||||
userCreate.password
|
||||
)
|
||||
)
|
||||
call.response.header("Location", "${Config.configData.url}/api/internal/v1/users/${user.name}")
|
||||
call.respond(HttpStatusCode.Created)
|
||||
}
|
||||
route("/{name}") {
|
||||
authenticate(TOKEN_AUTH, optional = true) {
|
||||
get {
|
||||
val userParameter = (
|
||||
call.parameters["name"]
|
||||
?: throw ParameterNotExistException(
|
||||
"Parameter(name='userName@domain') does not exist."
|
||||
)
|
||||
)
|
||||
if (userParameter.toLongOrNull() != null) {
|
||||
return@get call.respond(userApiService.findById(userParameter.toLong()))
|
||||
} else {
|
||||
val acct = AcctUtil.parse(userParameter)
|
||||
return@get call.respond(userApiService.findByAcct(acct))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
route("/followers") {
|
||||
get {
|
||||
val userParameter = call.parameters["name"]
|
||||
?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.")
|
||||
if (userParameter.toLongOrNull() != null) {
|
||||
return@get call.respond(userApiService.findFollowers(userParameter.toLong()))
|
||||
}
|
||||
val acct = AcctUtil.parse(userParameter)
|
||||
return@get call.respond(userApiService.findFollowersByAcct(acct))
|
||||
}
|
||||
authenticate(TOKEN_AUTH) {
|
||||
post {
|
||||
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
|
||||
?: throw IllegalStateException("no principal")
|
||||
val userParameter = call.parameters["name"]
|
||||
?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.")
|
||||
if (if (userParameter.toLongOrNull() != null) {
|
||||
userApiService.follow(userParameter.toLong(), userId)
|
||||
} else {
|
||||
val parse = AcctUtil.parse(userParameter)
|
||||
userApiService.follow(parse, userId)
|
||||
}
|
||||
) {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
} else {
|
||||
call.respond(HttpStatusCode.Accepted)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
route("/following") {
|
||||
get {
|
||||
val userParameter = (
|
||||
call.parameters["name"]
|
||||
?: throw ParameterNotExistException(
|
||||
"Parameter(name='userName@domain') does not exist."
|
||||
)
|
||||
)
|
||||
if (userParameter.toLongOrNull() != null) {
|
||||
return@get call.respond(userApiService.findFollowings(userParameter.toLong()))
|
||||
}
|
||||
val acct = AcctUtil.parse(userParameter)
|
||||
return@get call.respond(userApiService.findFollowingsByAcct(acct))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.api.mastodon.v1
|
||||
|
||||
// @Suppress("UnusedPrivateMember")
|
||||
// fun Route.statuses(postService: IPostService) {
|
||||
// // route("/statuses") {
|
||||
// // post {
|
||||
// // val status: StatusForPost = call.receive()
|
||||
// // val post = dev.usbharu.hideout.domain.model.hideout.form.Post(
|
||||
// // userId = status.userId,
|
||||
// // createdAt = System.currentTimeMillis(),
|
||||
// // text = status.status,
|
||||
// // visibility = 1
|
||||
// // )
|
||||
// // postService.create(post)
|
||||
// // call.respond(status)
|
||||
// // }
|
||||
// // }
|
||||
// }
|
|
@ -1,45 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.wellknown
|
||||
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.wellknown.WebFinger
|
||||
import dev.usbharu.hideout.exception.IllegalParameterException
|
||||
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||
import dev.usbharu.hideout.service.api.WebFingerApiService
|
||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
@Deprecated("Ktor is deprecated")
|
||||
fun Routing.webfinger(webFingerApiService: WebFingerApiService) {
|
||||
route("/.well-known/webfinger") {
|
||||
get {
|
||||
val acct = call.request.queryParameters["resource"]?.decodeURLPart()
|
||||
?: throw ParameterNotExistException("Parameter(name='resource') does not exist.")
|
||||
|
||||
if (acct.startsWith("acct:").not()) {
|
||||
throw IllegalParameterException("Parameter(name='resource') is not start with 'acct:'.")
|
||||
}
|
||||
|
||||
val accountName = acct.substringBeforeLast("@")
|
||||
.substringAfter("acct:")
|
||||
.trimStart('@')
|
||||
|
||||
val userEntity = webFingerApiService.findByNameAndDomain(accountName, Config.configData.domain)
|
||||
|
||||
val webFinger = WebFinger(
|
||||
subject = acct,
|
||||
links = listOf(
|
||||
WebFinger.Link(
|
||||
rel = "self",
|
||||
type = ContentType.Application.Activity.toString(),
|
||||
href = "${Config.configData.url}/users/${userEntity.name}"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return@get call.respond(webFinger)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,6 @@ interface AccountApiService {
|
|||
suspend fun verifyCredentials(userid: Long): CredentialAccount
|
||||
}
|
||||
|
||||
|
||||
@Service
|
||||
class AccountApiServiceImpl(private val accountService: AccountService, private val transaction: Transaction) :
|
||||
AccountApiService {
|
||||
|
@ -59,5 +58,4 @@ class AccountApiServiceImpl(private val accountService: AccountService, private
|
|||
role = Role(0, "Admin", "", 32)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ interface StatusesApiService {
|
|||
suspend fun postStatus(statusesRequest: StatusesRequest, user: UserDetailsImpl): Status
|
||||
}
|
||||
|
||||
|
||||
@Service
|
||||
class StatsesApiServiceImpl(
|
||||
private val postService: PostService,
|
||||
|
@ -28,7 +27,6 @@ class StatsesApiServiceImpl(
|
|||
) :
|
||||
StatusesApiService {
|
||||
override suspend fun postStatus(statusesRequest: StatusesRequest, user: UserDetailsImpl): Status {
|
||||
|
||||
val visibility = when (statusesRequest.visibility) {
|
||||
StatusesRequest.Visibility.public -> Visibility.PUBLIC
|
||||
StatusesRequest.Visibility.unlisted -> Visibility.UNLISTED
|
||||
|
@ -67,7 +65,6 @@ class StatsesApiServiceImpl(
|
|||
null
|
||||
}
|
||||
|
||||
|
||||
return Status(
|
||||
id = post.id.toString(),
|
||||
uri = post.apId,
|
||||
|
|
|
@ -26,11 +26,9 @@ class ExposedOAuth2AuthorizationConsentService(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun save(authorizationConsent: AuthorizationConsent?) = runBlocking {
|
||||
requireNotNull(authorizationConsent)
|
||||
transaction.transaction {
|
||||
|
||||
val singleOrNull =
|
||||
OAuth2AuthorizationConsent.select {
|
||||
OAuth2AuthorizationConsent.registeredClientId
|
||||
|
@ -61,7 +59,6 @@ class ExposedOAuth2AuthorizationConsentService(
|
|||
requireNotNull(registeredClientId)
|
||||
requireNotNull(principalName)
|
||||
transaction.transaction {
|
||||
|
||||
OAuth2AuthorizationConsent.select {
|
||||
(OAuth2AuthorizationConsent.registeredClientId eq registeredClientId)
|
||||
.and(OAuth2AuthorizationConsent.principalName eq principalName)
|
||||
|
|
|
@ -151,8 +151,6 @@ class ExposedOAuth2AuthorizationService(
|
|||
override fun findByToken(token: String?, tokenType: OAuth2TokenType?): OAuth2Authorization? = runBlocking {
|
||||
requireNotNull(token)
|
||||
transaction.transaction {
|
||||
|
||||
|
||||
when (tokenType?.value) {
|
||||
null -> {
|
||||
Authorization.select {
|
||||
|
|
|
@ -7,12 +7,10 @@ import java.util.*
|
|||
@Component
|
||||
class SecureTokenGeneratorImpl : SecureTokenGenerator {
|
||||
override fun generate(): String {
|
||||
|
||||
val byteArray = ByteArray(16)
|
||||
val secureRandom = SecureRandom()
|
||||
secureRandom.nextBytes(byteArray)
|
||||
|
||||
|
||||
return Base64.getUrlEncoder().encodeToString(byteArray)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,573 +0,0 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import com.auth0.jwk.Jwk
|
||||
import com.auth0.jwk.JwkProvider
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.config.ConfigData
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Jwt
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
|
||||
import dev.usbharu.hideout.exception.InvalidRefreshTokenException
|
||||
import dev.usbharu.hideout.exception.InvalidUsernameOrPasswordException
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.routing.api.internal.v1.auth
|
||||
import dev.usbharu.hideout.service.api.UserAuthApiService
|
||||
import dev.usbharu.hideout.service.auth.JwtService
|
||||
import dev.usbharu.hideout.service.core.MetaService
|
||||
import dev.usbharu.hideout.service.user.UserAuthService
|
||||
import dev.usbharu.hideout.util.Base64Util
|
||||
import dev.usbharu.hideout.util.JsonWebKeyUtil
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.config.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.testing.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.kotlin.*
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.interfaces.RSAPrivateKey
|
||||
import java.security.interfaces.RSAPublicKey
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class SecurityKtTest {
|
||||
@Test
|
||||
fun `login ログイン出来るか`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
|
||||
val jwtToken = JwtToken("Token", "RefreshToken")
|
||||
val userAuthService = mock<UserAuthApiService> {
|
||||
onBlocking { login(eq("testUser"), eq("password")) } doReturn jwtToken
|
||||
}
|
||||
val metaService = mock<MetaService>()
|
||||
val userQueryService = mock<UserQueryService> {
|
||||
onBlocking { findByNameAndDomain(eq("testUser"), eq("example.com")) } doReturn User.of(
|
||||
id = 1L,
|
||||
name = "testUser",
|
||||
domain = "example.com",
|
||||
screenName = "test",
|
||||
description = "",
|
||||
password = "hashedPassword",
|
||||
inbox = "https://example.com/inbox",
|
||||
outbox = "https://example.com/outbox",
|
||||
url = "https://example.com/profile",
|
||||
publicKey = "",
|
||||
privateKey = "",
|
||||
createdAt = Instant.now()
|
||||
)
|
||||
}
|
||||
val jwkProvider = mock<JwkProvider>()
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(jwkProvider, metaService)
|
||||
routing {
|
||||
auth(userAuthService)
|
||||
}
|
||||
}
|
||||
|
||||
client.post("/login") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(Config.configData.objectMapper.writeValueAsString(UserLogin("testUser", "password")))
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.OK, call.response.status)
|
||||
assertEquals(jwtToken, Config.configData.objectMapper.readValue(call.response.bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login 存在しないユーザーのログインに失敗する`() {
|
||||
testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
|
||||
mock<UserAuthService> {
|
||||
onBlocking { verifyAccount(anyString(), anyString()) }.doReturn(false)
|
||||
}
|
||||
val metaService = mock<MetaService>()
|
||||
mock<UserQueryService>()
|
||||
mock<JwtService>()
|
||||
val jwkProvider = mock<JwkProvider>()
|
||||
val userAuthApiService = mock<UserAuthApiService> {
|
||||
onBlocking { login(anyString(), anyString()) } doThrow InvalidUsernameOrPasswordException()
|
||||
}
|
||||
application {
|
||||
configureStatusPages()
|
||||
configureSerialization()
|
||||
configureSecurity(jwkProvider, metaService)
|
||||
routing {
|
||||
auth(userAuthApiService)
|
||||
}
|
||||
}
|
||||
client.post("/login") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(Config.configData.objectMapper.writeValueAsString(UserLogin("InvalidTtestUser", "password")))
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login 不正なパスワードのログインに失敗する`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
|
||||
val metaService = mock<MetaService>()
|
||||
val jwkProvider = mock<JwkProvider>()
|
||||
val userAuthApiService = mock<UserAuthApiService> {
|
||||
onBlocking { login(anyString(), eq("InvalidPassword")) } doThrow InvalidUsernameOrPasswordException()
|
||||
}
|
||||
application {
|
||||
configureStatusPages()
|
||||
configureSerialization()
|
||||
configureSecurity(jwkProvider, metaService)
|
||||
routing {
|
||||
auth(userAuthApiService)
|
||||
}
|
||||
}
|
||||
client.post("/login") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(Config.configData.objectMapper.writeValueAsString(UserLogin("TestUser", "InvalidPassword")))
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check Authorizedヘッダーが無いと401が帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
client.get("/auth-check").apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check Authorizedヘッダーの形式が間違っていると401が帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
client.get("/auth-check") {
|
||||
header("Authorization", "Digest dsfjjhogalkjdfmlhaog")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check Authorizedヘッダーが空だと401が帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
client.get("/auth-check") {
|
||||
header("Authorization", "")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check AuthorizedヘッダーがBeararで空だと401が帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
|
||||
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
client.get("/auth-check") {
|
||||
header("Authorization", "Bearer ")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check 正当なJWTだとアクセスできる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||
keyPairGenerator.initialize(2048)
|
||||
val keyPair = keyPairGenerator.generateKeyPair()
|
||||
val rsaPublicKey = keyPair.public as RSAPublicKey
|
||||
|
||||
Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper())
|
||||
|
||||
val now = Instant.now()
|
||||
val kid = UUID.randomUUID()
|
||||
val token = JWT.create()
|
||||
.withAudience("${Config.configData.url}/users/test")
|
||||
.withIssuer(Config.configData.url)
|
||||
.withKeyId(kid.toString())
|
||||
.withClaim("uid", 123456L)
|
||||
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
|
||||
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
|
||||
val metaService = mock<MetaService> {
|
||||
onBlocking { getJwtMeta() }.doReturn(
|
||||
Jwt(
|
||||
kid,
|
||||
Base64Util.encode(keyPair.private.encoded),
|
||||
Base64Util.encode(rsaPublicKey.encoded)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val readValue = Config.configData.objectMapper.readerFor(Map::class.java)
|
||||
.readValue<MutableMap<String, Any>?>(
|
||||
JsonWebKeyUtil.publicKeyToJwk(
|
||||
rsaPublicKey,
|
||||
kid.toString()
|
||||
)
|
||||
)
|
||||
val jwkProvider = mock<JwkProvider> {
|
||||
onBlocking { get(anyString()) }.doReturn(
|
||||
Jwk.fromValues(
|
||||
(readValue["keys"] as List<Map<String, Any>>)[0]
|
||||
)
|
||||
)
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(jwkProvider, metaService)
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/auth-check") {
|
||||
header("Authorization", "Bearer $token")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.OK, call.response.status)
|
||||
assertEquals("Hello 123456", call.response.bodyAsText())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check 期限切れのトークンではアクセスできない`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||
keyPairGenerator.initialize(2048)
|
||||
val keyPair = keyPairGenerator.generateKeyPair()
|
||||
val rsaPublicKey = keyPair.public as RSAPublicKey
|
||||
|
||||
Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper())
|
||||
|
||||
val now = Instant.now()
|
||||
val kid = UUID.randomUUID()
|
||||
val token = JWT.create()
|
||||
.withAudience("${Config.configData.url}/users/test")
|
||||
.withIssuer(Config.configData.url)
|
||||
.withKeyId(kid.toString())
|
||||
.withClaim("uid", 123345L)
|
||||
.withExpiresAt(now.minus(30, ChronoUnit.MINUTES))
|
||||
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
|
||||
val metaService = mock<MetaService> {
|
||||
onBlocking { getJwtMeta() }.doReturn(
|
||||
Jwt(
|
||||
kid,
|
||||
Base64Util.encode(keyPair.private.encoded),
|
||||
Base64Util.encode(rsaPublicKey.encoded)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val readValue = Config.configData.objectMapper.readerFor(Map::class.java)
|
||||
.readValue<MutableMap<String, Any>?>(
|
||||
JsonWebKeyUtil.publicKeyToJwk(
|
||||
rsaPublicKey,
|
||||
kid.toString()
|
||||
)
|
||||
)
|
||||
val jwkProvider = mock<JwkProvider> {
|
||||
onBlocking { get(anyString()) }.doReturn(
|
||||
Jwk.fromValues(
|
||||
(readValue["keys"] as List<Map<String, Any>>)[0]
|
||||
)
|
||||
)
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(jwkProvider, metaService)
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
client.get("/auth-check") {
|
||||
header("Authorization", "Bearer $token")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check issuerが間違っているとアクセスできない`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||
keyPairGenerator.initialize(2048)
|
||||
val keyPair = keyPairGenerator.generateKeyPair()
|
||||
val rsaPublicKey = keyPair.public as RSAPublicKey
|
||||
|
||||
Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper())
|
||||
|
||||
val now = Instant.now()
|
||||
val kid = UUID.randomUUID()
|
||||
val token = JWT.create()
|
||||
.withAudience("${Config.configData.url}/users/test")
|
||||
.withIssuer("https://example.com")
|
||||
.withKeyId(kid.toString())
|
||||
.withClaim("uid", 12345L)
|
||||
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
|
||||
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
|
||||
val metaService = mock<MetaService> {
|
||||
onBlocking { getJwtMeta() }.doReturn(
|
||||
Jwt(
|
||||
kid,
|
||||
Base64Util.encode(keyPair.private.encoded),
|
||||
Base64Util.encode(rsaPublicKey.encoded)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val readValue = Config.configData.objectMapper.readerFor(Map::class.java)
|
||||
.readValue<MutableMap<String, Any>?>(
|
||||
JsonWebKeyUtil.publicKeyToJwk(
|
||||
rsaPublicKey,
|
||||
kid.toString()
|
||||
)
|
||||
)
|
||||
val jwkProvider = mock<JwkProvider> {
|
||||
onBlocking { get(anyString()) }.doReturn(
|
||||
Jwk.fromValues(
|
||||
(readValue["keys"] as List<Map<String, Any>>)[0]
|
||||
)
|
||||
)
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(jwkProvider, metaService)
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
client.get("/auth-check") {
|
||||
header("Authorization", "Bearer $token")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check usernameが空だと失敗する`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||
keyPairGenerator.initialize(2048)
|
||||
val keyPair = keyPairGenerator.generateKeyPair()
|
||||
val rsaPublicKey = keyPair.public as RSAPublicKey
|
||||
|
||||
Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper())
|
||||
|
||||
val now = Instant.now()
|
||||
val kid = UUID.randomUUID()
|
||||
val token = JWT.create()
|
||||
.withAudience("${Config.configData.url}/users/test")
|
||||
.withIssuer(Config.configData.url)
|
||||
.withKeyId(kid.toString())
|
||||
.withClaim("uid", null as Long?)
|
||||
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
|
||||
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
|
||||
val metaService = mock<MetaService> {
|
||||
onBlocking { getJwtMeta() }.doReturn(
|
||||
Jwt(
|
||||
kid,
|
||||
Base64Util.encode(keyPair.private.encoded),
|
||||
Base64Util.encode(rsaPublicKey.encoded)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val readValue = Config.configData.objectMapper.readerFor(Map::class.java)
|
||||
.readValue<MutableMap<String, Any>?>(
|
||||
JsonWebKeyUtil.publicKeyToJwk(
|
||||
rsaPublicKey,
|
||||
kid.toString()
|
||||
)
|
||||
)
|
||||
val jwkProvider = mock<JwkProvider> {
|
||||
onBlocking { get(anyString()) }.doReturn(
|
||||
Jwk.fromValues(
|
||||
(readValue["keys"] as List<Map<String, Any>>)[0]
|
||||
)
|
||||
)
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(jwkProvider, metaService)
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
client.get("/auth-check") {
|
||||
header("Authorization", "Bearer $token")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `auth-check usernameが存在しないと失敗する`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||
keyPairGenerator.initialize(2048)
|
||||
val keyPair = keyPairGenerator.generateKeyPair()
|
||||
val rsaPublicKey = keyPair.public as RSAPublicKey
|
||||
|
||||
Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper())
|
||||
|
||||
val now = Instant.now()
|
||||
val kid = UUID.randomUUID()
|
||||
val token = JWT.create()
|
||||
.withAudience("${Config.configData.url}/users/test")
|
||||
.withIssuer(Config.configData.url)
|
||||
.withKeyId(kid.toString())
|
||||
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
|
||||
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
|
||||
val metaService = mock<MetaService> {
|
||||
onBlocking { getJwtMeta() }.doReturn(
|
||||
Jwt(
|
||||
kid,
|
||||
Base64Util.encode(keyPair.private.encoded),
|
||||
Base64Util.encode(rsaPublicKey.encoded)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val readValue = Config.configData.objectMapper.readerFor(Map::class.java)
|
||||
.readValue<MutableMap<String, Any>?>(
|
||||
JsonWebKeyUtil.publicKeyToJwk(
|
||||
rsaPublicKey,
|
||||
kid.toString()
|
||||
)
|
||||
)
|
||||
val jwkProvider = mock<JwkProvider> {
|
||||
onBlocking { get(anyString()) }.doReturn(
|
||||
Jwk.fromValues(
|
||||
(readValue["keys"] as List<Map<String, Any>>)[0]
|
||||
)
|
||||
)
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(jwkProvider, metaService)
|
||||
routing {
|
||||
auth(mock())
|
||||
}
|
||||
}
|
||||
client.get("/auth-check") {
|
||||
header("Authorization", "Bearer $token")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `refresh-token リフレッシュトークンが正当だとトークンを発行する`() = testApplication {
|
||||
Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper())
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val jwtService = mock<UserAuthApiService> {
|
||||
onBlocking { refreshToken(any()) }.doReturn(JwtToken("token", "refreshToken2"))
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
auth(jwtService)
|
||||
}
|
||||
}
|
||||
client.post("/refresh-token") {
|
||||
header("Content-Type", "application/json")
|
||||
setBody(Config.configData.objectMapper.writeValueAsString(RefreshToken("refreshToken")))
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.OK, call.response.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `refresh-token リフレッシュトークンが不正だと失敗する`() = testApplication {
|
||||
Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper())
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val jwtService = mock<UserAuthApiService> {
|
||||
onBlocking { refreshToken(any()) } doThrow InvalidRefreshTokenException("Invalid Refresh Token")
|
||||
}
|
||||
application {
|
||||
configureStatusPages()
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
auth(jwtService)
|
||||
}
|
||||
}
|
||||
client.post("/refresh-token") {
|
||||
header("Content-Type", "application/json")
|
||||
setBody(Config.configData.objectMapper.writeValueAsString(RefreshToken("InvalidRefreshToken")))
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.BadRequest, call.response.status)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.activitypub
|
||||
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.config.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.testing.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ContentTypeRouteSelectorTest {
|
||||
@Test
|
||||
fun `Content-Typeが一つでマッチする`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
application {
|
||||
routing {
|
||||
route("/test") {
|
||||
createChild(ContentTypeRouteSelector(ContentType.Application.Json)).handle {
|
||||
call.respondText("OK")
|
||||
}
|
||||
get {
|
||||
call.respondText("NG")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/test") {
|
||||
accept(ContentType.Text.Html)
|
||||
}.apply {
|
||||
assertEquals("NG", bodyAsText())
|
||||
}
|
||||
client.get("/test") {
|
||||
accept(ContentType.Application.Json)
|
||||
}.apply {
|
||||
assertEquals("OK", bodyAsText())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Content-Typeが一つのとき違うとマッチしない`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
application {
|
||||
routing {
|
||||
route("/test") {
|
||||
createChild(ContentTypeRouteSelector(ContentType.Application.Json)).handle {
|
||||
call.respondText("OK")
|
||||
}
|
||||
get {
|
||||
call.respondText("NG")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/test") {
|
||||
accept(ContentType.Text.Html)
|
||||
}.apply {
|
||||
assertEquals("NG", bodyAsText())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Content-Typeがからのときマッチしない`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
application {
|
||||
routing {
|
||||
route("/test") {
|
||||
createChild(ContentTypeRouteSelector()).handle {
|
||||
call.respondText("OK")
|
||||
}
|
||||
get {
|
||||
call.respondText("NG")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/test") {
|
||||
accept(ContentType.Text.Html)
|
||||
}.apply {
|
||||
assertEquals("NG", bodyAsText())
|
||||
}
|
||||
|
||||
client.get("/test").apply {
|
||||
assertEquals("NG", bodyAsText())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Content-Typeが複数指定されていてマッチする`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
application {
|
||||
routing {
|
||||
route("/test") {
|
||||
createChild(ContentTypeRouteSelector(ContentType.Application.Json, ContentType.Text.Html)).handle {
|
||||
call.respondText("OK")
|
||||
}
|
||||
get {
|
||||
call.respondText("NG")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/test") {
|
||||
accept(ContentType.Text.Html)
|
||||
}.apply {
|
||||
assertEquals("OK", bodyAsText())
|
||||
}
|
||||
|
||||
client.get("/test") {
|
||||
accept(ContentType.Application.Json)
|
||||
}.apply {
|
||||
assertEquals("OK", bodyAsText())
|
||||
}
|
||||
client.get("/test") {
|
||||
accept(ContentType.Application.Xml)
|
||||
}.apply {
|
||||
assertEquals("NG", bodyAsText())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.activitypub
|
||||
|
||||
import dev.usbharu.hideout.exception.JsonParseException
|
||||
import dev.usbharu.hideout.plugins.configureSerialization
|
||||
import dev.usbharu.hideout.plugins.configureStatusPages
|
||||
import dev.usbharu.hideout.service.ap.APService
|
||||
import dev.usbharu.hideout.service.ap.APUserService
|
||||
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
|
||||
import dev.usbharu.hideout.service.user.UserService
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.config.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.testing.*
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.doThrow
|
||||
import org.mockito.kotlin.mock
|
||||
|
||||
class InboxRoutingKtTest {
|
||||
@Test
|
||||
fun `sharedInboxにGETしたら405が帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
routing {
|
||||
inbox(mock(), mock())
|
||||
}
|
||||
}
|
||||
client.get("/inbox").let {
|
||||
Assertions.assertEquals(HttpStatusCode.MethodNotAllowed, it.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sharedInboxに空のリクエストボディでPOSTしたら400が帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {
|
||||
on { verify(any()) } doReturn true
|
||||
}
|
||||
val apService = mock<APService> {
|
||||
on { parseActivity(any()) } doThrow JsonParseException()
|
||||
}
|
||||
mock<UserService>()
|
||||
mock<APUserService>()
|
||||
application {
|
||||
configureStatusPages()
|
||||
configureSerialization()
|
||||
routing {
|
||||
inbox(httpSignatureVerifyService, apService)
|
||||
}
|
||||
}
|
||||
client.post("/inbox").let {
|
||||
Assertions.assertEquals(HttpStatusCode.BadRequest, it.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ユーザのinboxにGETしたら405が帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
routing {
|
||||
inbox(mock(), mock())
|
||||
}
|
||||
}
|
||||
client.get("/users/test/inbox").let {
|
||||
Assertions.assertEquals(HttpStatusCode.MethodNotAllowed, it.status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ユーザーのinboxに空のリクエストボディでPOSTしたら400が帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {
|
||||
on { verify(any()) } doReturn true
|
||||
}
|
||||
val apService = mock<APService> {
|
||||
on { parseActivity(any()) } doThrow JsonParseException()
|
||||
}
|
||||
mock<UserService>()
|
||||
mock<APUserService>()
|
||||
application {
|
||||
configureStatusPages()
|
||||
configureSerialization()
|
||||
routing {
|
||||
inbox(httpSignatureVerifyService, apService)
|
||||
}
|
||||
}
|
||||
client.post("/users/test/inbox").let {
|
||||
Assertions.assertEquals(HttpStatusCode.BadRequest, it.status)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.activitypub
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.annotation.JsonSetter
|
||||
import com.fasterxml.jackson.annotation.Nulls
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.domain.model.ap.Image
|
||||
import dev.usbharu.hideout.domain.model.ap.Key
|
||||
import dev.usbharu.hideout.domain.model.ap.Person
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
import dev.usbharu.hideout.plugins.configureSerialization
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.ap.APUserService
|
||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||
import dev.usbharu.hideout.util.HttpUtil.JsonLd
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.config.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.testing.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.eq
|
||||
import org.mockito.kotlin.mock
|
||||
import utils.TestTransaction
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class UsersAPTest {
|
||||
|
||||
@Test()
|
||||
fun `ユーザのURLにAcceptヘッダーをActivityにしてアクセスしたときPersonが返ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val person = Person(
|
||||
type = emptyList(),
|
||||
name = "test",
|
||||
id = "http://example.com/users/test",
|
||||
preferredUsername = "test",
|
||||
summary = "test user",
|
||||
inbox = "http://example.com/users/test/inbox",
|
||||
outbox = "http://example.com/users/test/outbox",
|
||||
url = "http://example.com/users/test",
|
||||
icon = Image(
|
||||
type = emptyList(),
|
||||
name = "http://example.com/users/test/icon.png",
|
||||
mediaType = "image/png",
|
||||
url = "http://example.com/users/test/icon.png"
|
||||
),
|
||||
publicKey = Key(
|
||||
type = emptyList(),
|
||||
name = "Public Key",
|
||||
id = "http://example.com/users/test#pubkey",
|
||||
owner = "https://example.com/users/test",
|
||||
publicKeyPem = "-----BEGIN PUBLIC KEY-----\n\n-----END PUBLIC KEY-----"
|
||||
)
|
||||
)
|
||||
person.context = listOf("https://www.w3.org/ns/activitystreams")
|
||||
|
||||
val apUserService = mock<APUserService> {
|
||||
onBlocking { getPersonByName(anyString()) } doReturn person
|
||||
}
|
||||
|
||||
application {
|
||||
configureSerialization()
|
||||
routing {
|
||||
usersAP(apUserService, mock(), mock(), TestTransaction)
|
||||
}
|
||||
}
|
||||
client.get("/users/test") {
|
||||
accept(ContentType.Application.Activity)
|
||||
}.let {
|
||||
val objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
objectMapper.configOverride(List::class.java).setSetterInfo(
|
||||
JsonSetter.Value.forValueNulls(
|
||||
Nulls.AS_EMPTY
|
||||
)
|
||||
)
|
||||
val actual = it.bodyAsText()
|
||||
val readValue = objectMapper.readValue<Person>(actual)
|
||||
assertEquals(person, readValue)
|
||||
}
|
||||
}
|
||||
|
||||
// @Disabled
|
||||
@Test()
|
||||
fun `ユーザのURLにAcceptヘッダーをActivityとJson-LDにしてアクセスしたときPersonが返ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val person = Person(
|
||||
type = emptyList(),
|
||||
name = "test",
|
||||
id = "http://example.com/users/test",
|
||||
preferredUsername = "test",
|
||||
summary = "test user",
|
||||
inbox = "http://example.com/users/test/inbox",
|
||||
outbox = "http://example.com/users/test/outbox",
|
||||
url = "http://example.com/users/test",
|
||||
icon = Image(
|
||||
type = emptyList(),
|
||||
name = "http://example.com/users/test/icon.png",
|
||||
mediaType = "image/png",
|
||||
url = "http://example.com/users/test/icon.png"
|
||||
),
|
||||
publicKey = Key(
|
||||
type = emptyList(),
|
||||
name = "Public Key",
|
||||
id = "http://example.com/users/test#pubkey",
|
||||
owner = "https://example.com/users/test",
|
||||
publicKeyPem = "-----BEGIN PUBLIC KEY-----\n\n-----END PUBLIC KEY-----"
|
||||
)
|
||||
)
|
||||
person.context = listOf("https://www.w3.org/ns/activitystreams")
|
||||
|
||||
val apUserService = mock<APUserService> {
|
||||
onBlocking { getPersonByName(anyString()) } doReturn person
|
||||
}
|
||||
|
||||
application {
|
||||
configureSerialization()
|
||||
routing {
|
||||
usersAP(apUserService, mock(), mock(), TestTransaction)
|
||||
}
|
||||
}
|
||||
client.get("/users/test") {
|
||||
accept(ContentType.Application.JsonLd)
|
||||
accept(ContentType.Application.Activity)
|
||||
}.let {
|
||||
val objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
objectMapper.configOverride(List::class.java).setSetterInfo(
|
||||
JsonSetter.Value.forValueNulls(
|
||||
Nulls.AS_EMPTY
|
||||
)
|
||||
)
|
||||
val actual = it.bodyAsText()
|
||||
val readValue = objectMapper.readValue<Person>(actual)
|
||||
assertEquals(person, readValue)
|
||||
}
|
||||
}
|
||||
|
||||
// @Disabled
|
||||
@Test
|
||||
fun contentType_Test() {
|
||||
assertTrue(ContentType.Application.Activity.match("application/activity+json"))
|
||||
val listOf = listOf(ContentType.Application.JsonLd, ContentType.Application.Activity)
|
||||
assertTrue(
|
||||
listOf.find { contentType ->
|
||||
contentType.match("application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
|
||||
}.let { it != null }
|
||||
)
|
||||
assertTrue(
|
||||
ContentType.Application.JsonLd.match(
|
||||
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ユーザーのURLにAcceptヘッダーをhtmlにしてアクセスしたときはただの文字を返す() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val userService = mock<UserQueryService> {
|
||||
onBlocking { findByNameAndDomain(eq("test"), anyString()) } doReturn User.of(
|
||||
1L,
|
||||
"test",
|
||||
"example.com",
|
||||
"test",
|
||||
"",
|
||||
"hashedPassword",
|
||||
"https://example.com/inbox",
|
||||
"https://example.com/outbox",
|
||||
"https://example.com",
|
||||
"-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----",
|
||||
"-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----",
|
||||
Instant.now()
|
||||
)
|
||||
}
|
||||
application {
|
||||
routing {
|
||||
usersAP(mock(), userService, mock(), TestTransaction)
|
||||
}
|
||||
}
|
||||
client.get("/users/test") {
|
||||
accept(ContentType.Text.Html)
|
||||
}.let {
|
||||
assertEquals(HttpStatusCode.OK, it.status)
|
||||
assertTrue(it.contentType()?.match(ContentType.Text.Plain) == true)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,734 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.api.internal.v1
|
||||
|
||||
import com.auth0.jwt.interfaces.Claim
|
||||
import com.auth0.jwt.interfaces.Payload
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.UserResponse
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
||||
import dev.usbharu.hideout.plugins.TOKEN_AUTH
|
||||
import dev.usbharu.hideout.plugins.configureSecurity
|
||||
import dev.usbharu.hideout.plugins.configureSerialization
|
||||
import dev.usbharu.hideout.service.api.PostApiService
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
import io.ktor.server.config.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.testing.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.*
|
||||
import utils.JsonObjectMapper
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertContentEquals
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class PostsTest {
|
||||
|
||||
@Test
|
||||
fun 認証情報無しでpostsにGETしたらPUBLICな投稿一覧が返ってくる() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
)
|
||||
val posts = listOf(
|
||||
PostResponse(
|
||||
id = "12345",
|
||||
user = user,
|
||||
text = "test1",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/1"
|
||||
),
|
||||
PostResponse(
|
||||
id = "123456",
|
||||
user = user,
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking {
|
||||
getAll(
|
||||
since = anyOrNull(),
|
||||
until = anyOrNull(),
|
||||
minId = anyOrNull(),
|
||||
maxId = anyOrNull(),
|
||||
limit = anyOrNull(),
|
||||
userId = isNull()
|
||||
)
|
||||
} doReturn posts
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/posts").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertContentEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun 認証情報ありでpostsにGETすると権限のある投稿が返ってくる() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val claim = mock<Claim> {
|
||||
on { asLong() } doReturn 1234
|
||||
}
|
||||
val payload = mock<Payload> {
|
||||
on { getClaim(eq("uid")) } doReturn claim
|
||||
}
|
||||
val user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
)
|
||||
val posts = listOf(
|
||||
PostResponse(
|
||||
id = "12345",
|
||||
user = user,
|
||||
text = "test1",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/1"
|
||||
),
|
||||
PostResponse(
|
||||
id = "123456",
|
||||
user = user,
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
),
|
||||
PostResponse(
|
||||
id = "1234567",
|
||||
user = user,
|
||||
text = "Followers only",
|
||||
visibility = Visibility.FOLLOWERS,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/3"
|
||||
)
|
||||
)
|
||||
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking {
|
||||
getAll(
|
||||
since = anyOrNull(),
|
||||
until = anyOrNull(),
|
||||
minId = anyOrNull(),
|
||||
maxId = anyOrNull(),
|
||||
limit = anyOrNull(),
|
||||
userId = isNotNull()
|
||||
)
|
||||
} doReturn posts
|
||||
}
|
||||
application {
|
||||
authentication {
|
||||
bearer(TOKEN_AUTH) {
|
||||
authenticate {
|
||||
JWTPrincipal(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
configureSerialization()
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
client.get("/api/internal/v1/posts") {
|
||||
header("Authorization", "Bearer asdkaf")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `posts id にGETしたらPUBLICな投稿を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
)
|
||||
val post = PostResponse(
|
||||
id = "12345",
|
||||
user = user,
|
||||
text = "aaa",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/1"
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking { getById(any(), anyOrNull()) } doReturn post
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
client.get("/api/internal/v1/posts/1").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `認証情報ありでposts id にGETしたら権限のある投稿を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val post = PostResponse(
|
||||
"12345",
|
||||
UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
),
|
||||
text = "aaa",
|
||||
visibility = Visibility.FOLLOWERS,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/1"
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking { getById(any(), isNotNull()) } doReturn post
|
||||
}
|
||||
val claim = mock<Claim> {
|
||||
on { asLong() } doReturn 1234
|
||||
}
|
||||
val payload = mock<Payload> {
|
||||
on { getClaim(eq("uid")) } doReturn claim
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
authentication {
|
||||
bearer(TOKEN_AUTH) {
|
||||
authenticate {
|
||||
JWTPrincipal(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
client.get("/api/internal/v1/posts/1") {
|
||||
header("Authorization", "Bearer asdkaf")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `posts-post postsにpostしたら投稿できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val claim = mock<Claim> {
|
||||
on { asLong() } doReturn 1234
|
||||
}
|
||||
val payload = mock<Payload> {
|
||||
on { getClaim(eq("uid")) } doReturn claim
|
||||
}
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking { createPost(any(), any()) } doAnswer {
|
||||
val argument = it.getArgument<dev.usbharu.hideout.domain.model.hideout.form.Post>(0)
|
||||
val userId = it.getArgument<Long>(1)
|
||||
PostResponse(
|
||||
id = "123",
|
||||
user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
),
|
||||
overview = null,
|
||||
text = argument.text,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
visibility = Visibility.PUBLIC,
|
||||
url = "https://example.com"
|
||||
)
|
||||
}
|
||||
}
|
||||
application {
|
||||
authentication {
|
||||
bearer(TOKEN_AUTH) {
|
||||
authenticate {
|
||||
println("aaaaaaaaaaaa")
|
||||
JWTPrincipal(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
configureSerialization()
|
||||
}
|
||||
|
||||
val post = dev.usbharu.hideout.domain.model.hideout.form.Post("test")
|
||||
client.post("/api/internal/v1/posts") {
|
||||
header("Authorization", "Bearer asdkaf")
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(Config.configData.objectMapper.writeValueAsString(post))
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals("https://example.com", headers["Location"])
|
||||
}
|
||||
argumentCaptor<dev.usbharu.hideout.domain.model.hideout.form.Post> {
|
||||
verify(postService).createPost(capture(), any())
|
||||
assertEquals(dev.usbharu.hideout.domain.model.hideout.form.Post("test"), firstValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users userId postsにGETしたらユーザーのPUBLICな投稿一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
)
|
||||
val posts = listOf(
|
||||
PostResponse(
|
||||
id = "12345",
|
||||
user = user,
|
||||
text = "test1",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/1"
|
||||
),
|
||||
PostResponse(
|
||||
id = "123456",
|
||||
user = user,
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking {
|
||||
getByUser(
|
||||
nameOrId = any(),
|
||||
since = anyOrNull(),
|
||||
until = anyOrNull(),
|
||||
minId = anyOrNull(),
|
||||
maxId = anyOrNull(),
|
||||
limit = anyOrNull(),
|
||||
userId = anyOrNull()
|
||||
)
|
||||
} doReturn posts
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/1/posts").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users username postsにGETしたらユーザーのPUBLICな投稿一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
)
|
||||
val posts = listOf(
|
||||
PostResponse(
|
||||
id = "12345",
|
||||
user = user,
|
||||
text = "test1",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/1"
|
||||
),
|
||||
PostResponse(
|
||||
id = "123456",
|
||||
user = user,
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking {
|
||||
getByUser(
|
||||
nameOrId = eq("test1"),
|
||||
since = anyOrNull(),
|
||||
until = anyOrNull(),
|
||||
minId = anyOrNull(),
|
||||
maxId = anyOrNull(),
|
||||
limit = anyOrNull(),
|
||||
userId = anyOrNull()
|
||||
)
|
||||
} doReturn posts
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test1/posts").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users username@domain postsにGETしたらユーザーのPUBLICな投稿一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
)
|
||||
val posts = listOf(
|
||||
PostResponse(
|
||||
id = "12345",
|
||||
user = user,
|
||||
text = "test1",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/1"
|
||||
),
|
||||
PostResponse(
|
||||
id = "123456",
|
||||
user = user,
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking {
|
||||
getByUser(
|
||||
nameOrId = eq("test1@example.com"),
|
||||
since = anyOrNull(),
|
||||
until = anyOrNull(),
|
||||
minId = anyOrNull(),
|
||||
maxId = anyOrNull(),
|
||||
limit = anyOrNull(),
|
||||
userId = anyOrNull()
|
||||
)
|
||||
} doReturn posts
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test1@example.com/posts").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users @username@domain postsにGETしたらユーザーのPUBLICな投稿一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
)
|
||||
val posts = listOf(
|
||||
PostResponse(
|
||||
id = "12345",
|
||||
user = user,
|
||||
text = "test1",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/1"
|
||||
),
|
||||
PostResponse(
|
||||
id = "123456",
|
||||
user = user,
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking {
|
||||
getByUser(
|
||||
nameOrId = eq("@test1@example.com"),
|
||||
since = anyOrNull(),
|
||||
until = anyOrNull(),
|
||||
minId = anyOrNull(),
|
||||
maxId = anyOrNull(),
|
||||
limit = anyOrNull(),
|
||||
userId = anyOrNull()
|
||||
)
|
||||
} doReturn posts
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/@test1@example.com/posts").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name posts id にGETしたらPUBLICな投稿を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val post = PostResponse(
|
||||
id = "123456",
|
||||
user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
),
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test/posts/12345").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users id posts id にGETしたらPUBLICな投稿を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val post = PostResponse(
|
||||
id = "123456",
|
||||
user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
),
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/1/posts/12345").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name posts id にGETしたらUserIdが間違っててもPUBLICな投稿を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val post = PostResponse(
|
||||
id = "123456",
|
||||
user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
),
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/423827849732847/posts/12345").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name posts id にGETしたらuserNameが間違っててもPUBLICな投稿を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val post = PostResponse(
|
||||
id = "123456",
|
||||
user = UserResponse(
|
||||
id = "54321",
|
||||
name = "user1",
|
||||
domain = "example.com",
|
||||
screenName = "user 1",
|
||||
description = "Test user",
|
||||
url = "https://example.com/users/54321",
|
||||
createdAt = Instant.now().toEpochMilli()
|
||||
),
|
||||
text = "test2",
|
||||
visibility = Visibility.PUBLIC,
|
||||
createdAt = Instant.now().toEpochMilli(),
|
||||
url = "https://example.com/posts/2"
|
||||
)
|
||||
val postService = mock<PostApiService> {
|
||||
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/invalidUserName/posts/12345").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,692 +0,0 @@
|
|||
package dev.usbharu.hideout.routing.api.internal.v1
|
||||
|
||||
import com.auth0.jwt.interfaces.Claim
|
||||
import com.auth0.jwt.interfaces.Payload
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.Acct
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.UserResponse
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
import dev.usbharu.hideout.domain.model.hideout.form.UserCreate
|
||||
import dev.usbharu.hideout.plugins.TOKEN_AUTH
|
||||
import dev.usbharu.hideout.plugins.configureSecurity
|
||||
import dev.usbharu.hideout.plugins.configureSerialization
|
||||
import dev.usbharu.hideout.service.api.UserApiService
|
||||
import dev.usbharu.hideout.service.user.UserService
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
import io.ktor.server.config.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.testing.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.*
|
||||
import utils.JsonObjectMapper
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertEquals
|
||||
@Suppress("LargeClass")
|
||||
class UsersTest {
|
||||
@Test
|
||||
fun `users にGETするとユーザー一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val users = listOf(
|
||||
UserResponse(
|
||||
"12345",
|
||||
"test1",
|
||||
"example.com",
|
||||
"test",
|
||||
"",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
),
|
||||
UserResponse(
|
||||
"12343",
|
||||
"tes2",
|
||||
"example.com",
|
||||
"test",
|
||||
"",
|
||||
"https://example.com/tes2",
|
||||
Instant.now().toEpochMilli()
|
||||
),
|
||||
)
|
||||
val userService = mock<UserApiService> {
|
||||
onBlocking { findAll(anyOrNull(), anyOrNull()) } doReturn users
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userService)
|
||||
}
|
||||
}
|
||||
}
|
||||
client.get("/api/internal/v1/users").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(users, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users にPOSTすると新規ユーザー作成ができる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val userCreateDto = UserCreate("test", "XXXXXXX")
|
||||
val userService = mock<UserService> {
|
||||
onBlocking { usernameAlreadyUse(any()) } doReturn false
|
||||
onBlocking { createLocalUser(any()) } doReturn User.of(
|
||||
id = 12345,
|
||||
name = "test",
|
||||
domain = "example.com",
|
||||
screenName = "testUser",
|
||||
description = "test user",
|
||||
password = "XXXXXXX",
|
||||
inbox = "https://example.com/inbox",
|
||||
outbox = "https://example.com/outbox",
|
||||
url = "https://example.com",
|
||||
publicKey = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----",
|
||||
privateKey = "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----",
|
||||
createdAt = Instant.now()
|
||||
)
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(userService, mock())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.post("/api/internal/v1/users") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(JsonObjectMapper.objectMapper.writeValueAsString(userCreateDto))
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Created, status)
|
||||
assertEquals(
|
||||
"${Config.configData.url}/api/internal/v1/users/${userCreateDto.username}",
|
||||
headers["Location"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users 既にユーザー名が使用されているときはBadRequestが帰ってくる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val userCreateDto = UserCreate("test", "XXXXXXX")
|
||||
val userService = mock<UserService> {
|
||||
onBlocking { usernameAlreadyUse(any()) } doReturn true
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(userService, mock())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.post("/api/internal/v1/users") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(JsonObjectMapper.objectMapper.writeValueAsString(userCreateDto))
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.BadRequest, status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name にGETしたらユーザーを取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val userResponse = UserResponse(
|
||||
"1234",
|
||||
"test1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findByAcct(any()) } doReturn userResponse
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test1").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(userResponse, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users id にGETしたらユーザーを取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val userResponse = UserResponse(
|
||||
"1234",
|
||||
"test1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findById(any()) } doReturn userResponse
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/1234").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(userResponse, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name@domain にGETしたらユーザーを取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val userResponse = UserResponse(
|
||||
"1234",
|
||||
"test1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findByAcct(any()) } doReturn userResponse
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test1").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(userResponse, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users @name@domain にGETしたらユーザーを取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
val userResponse = UserResponse(
|
||||
"1234",
|
||||
"test1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findByAcct(any()) } doReturn userResponse
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test1").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(userResponse, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name followers にGETしたらフォロワー一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val followers = listOf(
|
||||
UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
),
|
||||
UserResponse(
|
||||
"1236",
|
||||
"follower2",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findFollowersByAcct(any()) } doReturn followers
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test1/followers").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name@domain followers にGETしたらフォロワー一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val followers = listOf(
|
||||
UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
),
|
||||
UserResponse(
|
||||
"1236",
|
||||
"follower2",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findFollowersByAcct(any()) } doReturn followers
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/@test1@example.com/followers").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users id followers にGETしたらフォロワー一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val followers = listOf(
|
||||
UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
),
|
||||
UserResponse(
|
||||
"1236",
|
||||
"follower2",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findFollowers(any()) } doReturn followers
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/1234/followers").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name followers に認証情報ありでGETしたらフォローできる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val claim = mock<Claim> {
|
||||
on { asLong() } doReturn 1234
|
||||
}
|
||||
val payload = mock<Payload> {
|
||||
on { getClaim(eq("uid")) } doReturn claim
|
||||
}
|
||||
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findByAcct(any()) } doReturn UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
onBlocking { follow(any<Acct>(), eq(1234)) } doReturn true
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
authentication {
|
||||
bearer(TOKEN_AUTH) {
|
||||
authenticate {
|
||||
JWTPrincipal(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.post("/api/internal/v1/users/test1/followers") {
|
||||
header(HttpHeaders.Authorization, "Bearer test")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name followers に認証情報ありでGETしたらフォロー処理受付になることもある`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val claim = mock<Claim> {
|
||||
on { asLong() } doReturn 1234
|
||||
}
|
||||
val payload = mock<Payload> {
|
||||
on { getClaim(eq("uid")) } doReturn claim
|
||||
}
|
||||
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findByAcct(any()) } doReturn UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
onBlocking { follow(any<Acct>(), eq(1234)) } doReturn false
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
authentication {
|
||||
bearer(TOKEN_AUTH) {
|
||||
authenticate {
|
||||
JWTPrincipal(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.post("/api/internal/v1/users/test1/followers") {
|
||||
header(HttpHeaders.Authorization, "Bearer test")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Accepted, status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users id followers に認証情報ありでGETしたらフォロー処理受付になることもある`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val claim = mock<Claim> {
|
||||
on { asLong() } doReturn 1234
|
||||
}
|
||||
val payload = mock<Payload> {
|
||||
on { getClaim(eq("uid")) } doReturn claim
|
||||
}
|
||||
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findById(any()) } doReturn UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
onBlocking { follow(eq(1235), eq(1234)) } doReturn false
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
authentication {
|
||||
bearer(TOKEN_AUTH) {
|
||||
authenticate {
|
||||
JWTPrincipal(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.post("/api/internal/v1/users/1235/followers") {
|
||||
header(HttpHeaders.Authorization, "Bearer test")
|
||||
}.apply {
|
||||
assertEquals(HttpStatusCode.Accepted, status)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name following にGETしたらフォロイー一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val followers = listOf(
|
||||
UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
),
|
||||
UserResponse(
|
||||
"1236",
|
||||
"follower2",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findFollowingsByAcct(any()) } doReturn followers
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test1/following").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users name@domain following にGETしたらフォロイー一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val followers = listOf(
|
||||
UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
),
|
||||
UserResponse(
|
||||
"1236",
|
||||
"follower2",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findFollowingsByAcct(any()) } doReturn followers
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/test1@domain/following").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users id following にGETしたらフォロイー一覧を取得できる`() = testApplication {
|
||||
environment {
|
||||
config = ApplicationConfig("empty.conf")
|
||||
}
|
||||
|
||||
val followers = listOf(
|
||||
UserResponse(
|
||||
"1235",
|
||||
"follower1",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
),
|
||||
UserResponse(
|
||||
"1236",
|
||||
"follower2",
|
||||
"example.com",
|
||||
"test",
|
||||
"test User",
|
||||
"https://example.com/test",
|
||||
Instant.now().toEpochMilli()
|
||||
)
|
||||
)
|
||||
val userApiService = mock<UserApiService> {
|
||||
onBlocking { findFollowings(any()) } doReturn followers
|
||||
}
|
||||
application {
|
||||
configureSerialization()
|
||||
configureSecurity(mock(), mock())
|
||||
routing {
|
||||
route("/api/internal/v1") {
|
||||
users(mock(), userApiService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.get("/api/internal/v1/users/1234/following").apply {
|
||||
assertEquals(HttpStatusCode.OK, status)
|
||||
assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText()))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue