diff --git a/src/main/kotlin/dev/usbharu/hideout/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/config/SecurityConfig.kt
index 589dc701..f98420b3 100644
--- a/src/main/kotlin/dev/usbharu/hideout/config/SecurityConfig.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/config/SecurityConfig.kt
@@ -12,7 +12,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.annotation.Order
-import org.springframework.http.MediaType
import org.springframework.security.config.Customizer
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
@@ -28,7 +27,6 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher
-import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher
import org.springframework.web.servlet.handler.HandlerMappingIntrospector
import java.security.KeyPairGenerator
import java.security.interfaces.RSAPrivateKey
@@ -47,9 +45,8 @@ class SecurityConfig {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
http
.exceptionHandling {
- it.defaultAuthenticationEntryPointFor(
- LoginUrlAuthenticationEntryPoint("/login"),
- MediaTypeRequestMatcher(MediaType.TEXT_HTML)
+ it.authenticationEntryPoint(
+ LoginUrlAuthenticationEntryPoint("/login")
)
}
.oauth2ResourceServer {
@@ -58,34 +55,33 @@ class SecurityConfig {
return http.build()
}
+
@Bean
@Order(2)
fun defaultSecurityFilterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain {
val builder = MvcRequestMatcher.Builder(introspector)
- http.authorizeHttpRequests {
- it.requestMatchers(builder.pattern("/api/v1/**")).hasAnyAuthority("SCOPE_read", "SCOPE_read:accounts")
- }
- http
- .authorizeHttpRequests {
- it.requestMatchers(
- builder.pattern("/inbox"),
- builder.pattern("/api/v1/apps"),
- builder.pattern("/api/v1/instance/**")
- ).permitAll()
- }
http
.authorizeHttpRequests {
it.requestMatchers(PathRequest.toH2Console()).permitAll()
- }
- http
- .authorizeHttpRequests {
- it.anyRequest().authenticated()
+ it.requestMatchers(
+ builder.pattern("/inbox"),
+ builder.pattern("/api/v1/apps"),
+ builder.pattern("/api/v1/instance/**"),
+ builder.pattern("/.well-known/**"),
+ builder.pattern("/error"),
+ builder.pattern("/nodeinfo/2.0")
+ ).permitAll()
+ it.requestMatchers(builder.pattern("/change-password")).authenticated()
+ it.requestMatchers(builder.pattern("/api/v1/accounts/verify_credentials"))
+ .hasAnyAuthority("SCOPE_read", "SCOPE_read:accounts")
+ it.anyRequest().permitAll()
}
http
.oauth2ResourceServer {
it.jwt(Customizer.withDefaults())
}
+ .passwordManagement { }
.formLogin(Customizer.withDefaults())
.csrf {
it.ignoringRequestMatchers(builder.pattern("/api/**"))
diff --git a/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/HostMetaController.kt b/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/HostMetaController.kt
new file mode 100644
index 00000000..1c92dc73
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/HostMetaController.kt
@@ -0,0 +1,42 @@
+package dev.usbharu.hideout.controller.wellknown
+
+import dev.usbharu.hideout.config.ApplicationConfig
+import org.intellij.lang.annotations.Language
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+class HostMetaController(private val applicationConfig: ApplicationConfig) {
+
+ val xml = //language=XML
+ """
+
+
+"""
+
+ @Language("JSON")
+ val json = """{
+ "links": [
+ {
+ "rel": "lrdd",
+ "type": "application/jrd+json",
+ "template": "${applicationConfig.url}/.well-known/webfinger?resource={uri}"
+ }
+ ]
+}"""
+
+ @GetMapping("/.well-known/host-meta", produces = ["application/xml"])
+ fun hostmeta(): ResponseEntity {
+ return ResponseEntity(xml, HttpStatus.OK)
+ }
+
+ @GetMapping("/.well-known/host-meta.json", produces = ["application/json"])
+ fun hostmetJson(): ResponseEntity {
+ return ResponseEntity(json, HttpStatus.OK)
+ }
+
+
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/NodeinfoController.kt b/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/NodeinfoController.kt
new file mode 100644
index 00000000..a6630fe7
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/NodeinfoController.kt
@@ -0,0 +1,64 @@
+package dev.usbharu.hideout.controller.wellknown
+
+import dev.usbharu.hideout.config.ApplicationConfig
+import dev.usbharu.hideout.domain.model.wellknown.Nodeinfo
+import dev.usbharu.hideout.domain.model.wellknown.Nodeinfo2_0
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+class NodeinfoController(private val applicationConfig: ApplicationConfig) {
+ @GetMapping("/.well-known/nodeinfo")
+ fun nodeinfo(): ResponseEntity {
+ return ResponseEntity(
+ Nodeinfo(
+ listOf(
+ Nodeinfo.Links(
+ "http://nodeinfo.diaspora.software/ns/schema/2.0",
+ "${applicationConfig.url}/nodeinfo/2.0"
+ )
+ )
+ ), HttpStatus.OK
+ )
+ }
+
+ @GetMapping("/nodeinfo/2.0")
+ fun nodeinfo2_0(): ResponseEntity {
+ return ResponseEntity(
+ Nodeinfo2_0(
+ version = "2.0",
+ software = Nodeinfo2_0.Software(
+ name = "hideout",
+ version = "0.0.1"
+ ),
+ protocols = listOf("activitypub"),
+ services = Nodeinfo2_0.Services(
+ inbound = emptyList(),
+ outbound = emptyList()
+ ),
+ openRegistrations = false,
+ usage = Nodeinfo2_0.Usage(
+ users = Nodeinfo2_0.Usage.Users(
+ total = 1,
+ activeHalfYear = 1,
+ activeMonth = 1
+ ),
+ localPosts = 1,
+ localComments = 0
+ ),
+ metadata = Nodeinfo2_0.Metadata(
+ nodeName = "hideout",
+ nodeDescription = "hideout test server",
+ maintainer = Nodeinfo2_0.Metadata.Maintainer("usbharu", "i@usbharu.dev"),
+ langs = emptyList(),
+ tosUrl = "",
+ repositoryUrl = "https://github.com/usbharu/Hideout",
+ feedbackUrl = "https://github.com/usbharu/Hideout/issues/new/choose",
+ )
+ ),
+ HttpStatus.OK
+ )
+ }
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/WebFingerController.kt b/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/WebFingerController.kt
new file mode 100644
index 00000000..3ea7fffd
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/controller/wellknown/WebFingerController.kt
@@ -0,0 +1,37 @@
+package dev.usbharu.hideout.controller.wellknown
+
+import dev.usbharu.hideout.config.ApplicationConfig
+import dev.usbharu.hideout.domain.model.wellknown.WebFinger
+import dev.usbharu.hideout.service.api.WebFingerApiService
+import dev.usbharu.hideout.util.AcctUtil
+import kotlinx.coroutines.runBlocking
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.stereotype.Controller
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RequestParam
+import java.net.URL
+
+@Controller
+class WebFingerController(
+ private val webFingerApiService: WebFingerApiService,
+ private val applicationConfig: ApplicationConfig
+) {
+ @GetMapping("/.well-known/webfinger")
+ fun webfinger(@RequestParam("resource") resource: String): ResponseEntity = runBlocking {
+ val acct = AcctUtil.parse(resource.replace("acct:", ""))
+ val user =
+ webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: URL(applicationConfig.url).host)
+ val webFinger = WebFinger(
+ "acct:${user.name}@${user.domain}",
+ listOf(
+ WebFinger.Link(
+ "self",
+ "application/activity+json",
+ applicationConfig.url + "/users/" + user.id
+ )
+ )
+ )
+ ResponseEntity(webFinger, HttpStatus.OK)
+ }
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/wellknown/Nodeinfo.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/wellknown/Nodeinfo.kt
new file mode 100644
index 00000000..46adc8b8
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/wellknown/Nodeinfo.kt
@@ -0,0 +1,11 @@
+package dev.usbharu.hideout.domain.model.wellknown
+
+
+data class Nodeinfo(
+ val links: List
+) {
+ data class Links(
+ val rel: String,
+ val href: String
+ )
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/wellknown/Nodeinfo2_0.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/wellknown/Nodeinfo2_0.kt
new file mode 100644
index 00000000..fbce0298
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/wellknown/Nodeinfo2_0.kt
@@ -0,0 +1,48 @@
+package dev.usbharu.hideout.domain.model.wellknown
+
+data class Nodeinfo2_0(
+ val version: String,
+ val software: Software,
+ val protocols: List,
+ val services: Services,
+ val openRegistrations: Boolean,
+ val usage: Usage,
+ val metadata: Metadata
+) {
+ data class Software(
+ val name: String,
+ val version: String
+ )
+
+ data class Services(
+ val inbound: List,
+ val outbound: List
+ )
+
+ data class Usage(
+ val users: Users,
+ val localPosts: Int,
+ val localComments: Int
+ ) {
+ data class Users(
+ val total: Int,
+ val activeHalfYear: Int,
+ val activeMonth: Int
+ )
+ }
+
+ data class Metadata(
+ val nodeName: String,
+ val nodeDescription: String,
+ val maintainer: Maintainer,
+ val langs: List,
+ val tosUrl: String,
+ val repositoryUrl: String,
+ val feedbackUrl: String,
+ ) {
+ data class Maintainer(
+ val name: String,
+ val email: String
+ )
+ }
+}