feat: 投稿できるように

This commit is contained in:
usbharu 2023-07-06 22:06:10 +09:00
parent 539a9cf41f
commit 58f8005ce4
10 changed files with 151 additions and 109 deletions

View File

@ -49,19 +49,19 @@ val Application.property: Application.(propertyName: String) -> String
@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)
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)
)
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")
url = property("hideout.database.url"),
driver = property("hideout.database.driver"),
user = property("hideout.database.username"),
password = property("hideout.database.password")
)
}
single<JobQueueParentService> {
@ -84,11 +84,11 @@ fun Application.parent() {
single<IdGenerateService> { TwitterSnowflakeIdGenerateService }
single<JwkProvider> {
JwkProviderBuilder(Config.configData.url).cached(
10,
24,
TimeUnit.HOURS
10,
24,
TimeUnit.HOURS
)
.rateLimited(10, 1, TimeUnit.MINUTES).build()
.rateLimited(10, 1, TimeUnit.MINUTES).build()
}
}
configureKoin(module, HideoutModule().module)
@ -102,19 +102,20 @@ fun Application.parent() {
configureSerialization()
register(inject<IUserService>().value)
configureSecurity(
inject<IUserAuthService>().value,
inject<IMetaService>().value,
inject<IUserRepository>().value,
inject<IJwtService>().value,
inject<JwkProvider>().value,
inject<JwkProvider>().value,
inject<IMetaService>().value
)
configureRouting(
httpSignatureVerifyService = inject<HttpSignatureVerifyService>().value,
activityPubService = inject<ActivityPubService>().value,
userService = inject<IUserService>().value,
activityPubUserService = inject<ActivityPubUserService>().value,
postService = inject<IPostApiService>().value,
userApiService = inject<IUserApiService>().value,
httpSignatureVerifyService = inject<HttpSignatureVerifyService>().value,
activityPubService = inject<ActivityPubService>().value,
userService = inject<IUserService>().value,
activityPubUserService = inject<ActivityPubUserService>().value,
postService = inject<IPostApiService>().value,
userApiService = inject<IUserApiService>().value,
userAuthService = inject<IUserAuthService>().value,
userRepository = inject<IUserRepository>().value,
jwtService = inject<IJwtService>().value,
metaService = inject<IMetaService>().value
)
}

View File

@ -1,8 +1,10 @@
package dev.usbharu.hideout.plugins
import dev.usbharu.hideout.repository.IUserRepository
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
@ -11,6 +13,9 @@ import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
import dev.usbharu.hideout.service.api.IPostApiService
import dev.usbharu.hideout.service.api.IUserApiService
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
import dev.usbharu.hideout.service.auth.IJwtService
import dev.usbharu.hideout.service.core.IMetaService
import dev.usbharu.hideout.service.user.IUserAuthService
import dev.usbharu.hideout.service.user.IUserService
import io.ktor.server.application.*
import io.ktor.server.plugins.autohead.*
@ -18,12 +23,16 @@ import io.ktor.server.routing.*
@Suppress("LongParameterList")
fun Application.configureRouting(
httpSignatureVerifyService: HttpSignatureVerifyService,
activityPubService: ActivityPubService,
userService: IUserService,
activityPubUserService: ActivityPubUserService,
postService: IPostApiService,
userApiService: IUserApiService
httpSignatureVerifyService: HttpSignatureVerifyService,
activityPubService: ActivityPubService,
userService: IUserService,
activityPubUserService: ActivityPubUserService,
postService: IPostApiService,
userApiService: IUserApiService,
userAuthService: IUserAuthService,
userRepository: IUserRepository,
jwtService: IJwtService,
metaService: IMetaService
) {
install(AutoHeadResponse)
routing {
@ -34,6 +43,7 @@ fun Application.configureRouting(
route("/api/internal/v1") {
posts(postService)
users(userService, userApiService)
auth(userAuthService, userRepository, jwtService, metaService)
}
}
}

View File

@ -2,19 +2,12 @@ package dev.usbharu.hideout.plugins
import com.auth0.jwk.JwkProvider
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
import dev.usbharu.hideout.exception.UserNotFoundException
import dev.usbharu.hideout.repository.IUserRepository
import dev.usbharu.hideout.service.auth.IJwtService
import dev.usbharu.hideout.service.core.IMetaService
import dev.usbharu.hideout.service.user.IUserAuthService
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.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
@ -22,11 +15,8 @@ const val TOKEN_AUTH = "jwt-auth"
@Suppress("MagicNumber")
fun Application.configureSecurity(
userAuthService: IUserAuthService,
metaService: IMetaService,
userRepository: IUserRepository,
jwtService: IJwtService,
jwkProvider: JwkProvider
jwkProvider: JwkProvider,
metaService: IMetaService
) {
val issuer = Config.configData.url
install(Authentication) {
@ -48,38 +38,13 @@ fun Application.configureSecurity(
}
routing {
post("/login") {
val loginUser = call.receive<UserLogin>()
val check = userAuthService.verifyAccount(loginUser.username, loginUser.password)
if (check.not()) {
return@post call.respond(HttpStatusCode.Unauthorized)
}
val user = userRepository.findByNameAndDomain(loginUser.username, Config.configData.domain)
?: throw UserNotFoundException("${loginUser.username} was not found.")
return@post call.respond(jwtService.createToken(user))
}
post("/refresh-token") {
val refreshToken = call.receive<RefreshToken>()
return@post call.respond(jwtService.refreshToken(refreshToken))
}
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())
contentType = ContentType.Application.Json,
text = JsonWebKeyUtil.publicKeyToJwk(jwt.publicKey, jwt.kid.toString())
)
}
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")
}
}
}
}

View File

@ -0,0 +1,49 @@
package dev.usbharu.hideout.routing.api.internal.v1
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
import dev.usbharu.hideout.exception.UserNotFoundException
import dev.usbharu.hideout.plugins.TOKEN_AUTH
import dev.usbharu.hideout.repository.IUserRepository
import dev.usbharu.hideout.service.auth.IJwtService
import dev.usbharu.hideout.service.core.IMetaService
import dev.usbharu.hideout.service.user.IUserAuthService
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.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Route.auth(userAuthService: IUserAuthService,
userRepository: IUserRepository,
jwtService: IJwtService,
metaService: IMetaService) {
post("/login") {
val loginUser = call.receive<UserLogin>()
val check = userAuthService.verifyAccount(loginUser.username, loginUser.password)
if (check.not()) {
return@post call.respond(HttpStatusCode.Unauthorized)
}
val user = userRepository.findByNameAndDomain(loginUser.username, Config.configData.domain)
?: throw UserNotFoundException("${loginUser.username} was not found.")
return@post call.respond(jwtService.createToken(user))
}
post("/refresh-token") {
val refreshToken = call.receive<RefreshToken>()
return@post call.respond(jwtService.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")
}
}
}

View File

@ -425,3 +425,4 @@ components:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT

View File

@ -1,16 +1,28 @@
import {Component, createSignal, lazy} from "solid-js";
import {Component, createEffect, createSignal} from "solid-js";
import {Route, Router, Routes} from "@solidjs/router";
import {TopPage} from "./pages/TopPage";
import {createTheme, CssBaseline, ThemeProvider, useMediaQuery} from "@suid/material";
import {createCookieStorage} from "@solid-primitives/storage";
import {ApiProvider, useApi} from "./lib/ApiProvider";
import {ApiProvider} from "./lib/ApiProvider";
import {Configuration, DefaultApi} from "./generated";
import {LoginPage} from "./pages/LoginPage";
export const App: Component = () => {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const [cookie,setCookie] = createCookieStorage()
const [api,setApi] = createSignal(new DefaultApi(new Configuration({basePath:window.location.origin+"/api/internal/v1",apiKey:cookie.key as string})))
const [cookie, setCookie] = createCookieStorage()
const [api, setApi] = createSignal(new DefaultApi(new Configuration({
basePath: window.location.origin + "/api/internal/v1",
accessToken: cookie.token as string
})))
createEffect(() => {
setApi(
new DefaultApi(new Configuration({
basePath: window.location.origin + "/api/internal/v1",
accessToken : cookie.token as string
})))
})
const theme = createTheme({
palette: {
mode: prefersDarkMode() ? 'dark' : 'light',

View File

@ -2,6 +2,7 @@ import {Button, Card, CardContent, CardHeader, Modal, Stack, TextField} from "@s
import {Component, createSignal} from "solid-js";
import {createCookieStorage} from "@solid-primitives/storage";
import {useApi} from "../lib/ApiProvider";
import {useNavigate} from "@solidjs/router";
export const LoginPage: Component = () => {
const [username, setUsername] = createSignal("")
@ -9,12 +10,15 @@ export const LoginPage: Component = () => {
const [cookie, setCookie] = createCookieStorage();
const navigator = useNavigate();
const api = useApi();
const onSubmit: () => void = () => {
api().loginPost({password: password(), username: username()}).then(value => {
setCookie("token", value.token);
setCookie("refresh-token", value.refreshToken)
navigator("/")
}).catch(reason => {
console.log(reason);
setPassword("")

View File

@ -70,7 +70,7 @@ class SecurityKtTest {
val jwkProvider = mock<JwkProvider>()
application {
configureSerialization()
configureSecurity(userAuthService, metaService, userRepository, jwtService, jwkProvider)
configureSecurity(jwkProvider)
}
client.post("/login") {
@ -97,7 +97,7 @@ class SecurityKtTest {
val jwkProvider = mock<JwkProvider>()
application {
configureSerialization()
configureSecurity(userAuthService, metaService, userRepository, jwtService, jwkProvider)
configureSecurity(jwkProvider)
}
client.post("/login") {
contentType(ContentType.Application.Json)
@ -122,7 +122,7 @@ class SecurityKtTest {
val jwkProvider = mock<JwkProvider>()
application {
configureSerialization()
configureSecurity(userAuthService, metaService, userRepository, jwtService, jwkProvider)
configureSecurity(jwkProvider)
}
client.post("/login") {
contentType(ContentType.Application.Json)
@ -140,7 +140,7 @@ class SecurityKtTest {
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
}
client.get("/auth-check").apply {
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
@ -155,7 +155,7 @@ class SecurityKtTest {
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
}
client.get("/auth-check") {
header("Authorization", "Digest dsfjjhogalkjdfmlhaog")
@ -172,7 +172,7 @@ class SecurityKtTest {
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
}
client.get("/auth-check") {
header("Authorization", "")
@ -190,7 +190,7 @@ class SecurityKtTest {
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
}
client.get("/auth-check") {
header("Authorization", "Bearer ")
@ -248,7 +248,7 @@ class SecurityKtTest {
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
configureSecurity(jwkProvider)
}
client.get("/auth-check") {
@ -308,7 +308,7 @@ class SecurityKtTest {
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
configureSecurity(jwkProvider)
}
client.get("/auth-check") {
header("Authorization", "Bearer $token")
@ -366,7 +366,7 @@ class SecurityKtTest {
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
configureSecurity(jwkProvider)
}
client.get("/auth-check") {
header("Authorization", "Bearer $token")
@ -424,7 +424,7 @@ class SecurityKtTest {
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
configureSecurity(jwkProvider)
}
client.get("/auth-check") {
header("Authorization", "Bearer $token")
@ -481,7 +481,7 @@ class SecurityKtTest {
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
configureSecurity(jwkProvider)
}
client.get("/auth-check") {
header("Authorization", "Bearer $token")
@ -501,7 +501,7 @@ class SecurityKtTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), jwtService, mock())
configureSecurity(mock())
}
client.post("/refresh-token") {
header("Content-Type", "application/json")
@ -523,7 +523,7 @@ class SecurityKtTest {
application {
configureStatusPages()
configureSerialization()
configureSecurity(mock(), mock(), mock(), jwtService, mock())
configureSecurity(mock())
}
client.post("/refresh-token") {
header("Content-Type", "application/json")

View File

@ -64,7 +64,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -169,7 +169,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -323,7 +323,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -375,7 +375,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -427,7 +427,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -479,7 +479,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -511,7 +511,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -543,7 +543,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -575,7 +575,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -607,7 +607,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
posts(postService)

View File

@ -58,7 +58,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userService)
@ -96,7 +96,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(userService, mock())
@ -127,7 +127,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(userService, mock())
@ -162,7 +162,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -195,7 +195,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -228,7 +228,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -261,7 +261,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -306,7 +306,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -351,7 +351,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -396,7 +396,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -591,7 +591,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -636,7 +636,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -681,7 +681,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock(), mock(), mock(), mock())
configureSecurity(mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)