mirror of https://github.com/usbharu/Hideout.git
Merge pull request #48 from usbharu/feature/wellknown-endpoin
Feature/wellknown endpoint
This commit is contained in:
commit
a0ffd3cfe1
|
@ -12,7 +12,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.core.annotation.Order
|
import org.springframework.core.annotation.Order
|
||||||
import org.springframework.http.MediaType
|
|
||||||
import org.springframework.security.config.Customizer
|
import org.springframework.security.config.Customizer
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
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.SecurityFilterChain
|
||||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher
|
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 org.springframework.web.servlet.handler.HandlerMappingIntrospector
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.interfaces.RSAPrivateKey
|
import java.security.interfaces.RSAPrivateKey
|
||||||
|
@ -47,9 +45,8 @@ class SecurityConfig {
|
||||||
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
|
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
|
||||||
http
|
http
|
||||||
.exceptionHandling {
|
.exceptionHandling {
|
||||||
it.defaultAuthenticationEntryPointFor(
|
it.authenticationEntryPoint(
|
||||||
LoginUrlAuthenticationEntryPoint("/login"),
|
LoginUrlAuthenticationEntryPoint("/login")
|
||||||
MediaTypeRequestMatcher(MediaType.TEXT_HTML)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.oauth2ResourceServer {
|
.oauth2ResourceServer {
|
||||||
|
@ -58,34 +55,33 @@ class SecurityConfig {
|
||||||
return http.build()
|
return http.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(2)
|
@Order(2)
|
||||||
fun defaultSecurityFilterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain {
|
fun defaultSecurityFilterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain {
|
||||||
val builder = MvcRequestMatcher.Builder(introspector)
|
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
|
http
|
||||||
.authorizeHttpRequests {
|
.authorizeHttpRequests {
|
||||||
it.requestMatchers(PathRequest.toH2Console()).permitAll()
|
it.requestMatchers(PathRequest.toH2Console()).permitAll()
|
||||||
}
|
it.requestMatchers(
|
||||||
http
|
builder.pattern("/inbox"),
|
||||||
.authorizeHttpRequests {
|
builder.pattern("/api/v1/apps"),
|
||||||
it.anyRequest().authenticated()
|
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
|
http
|
||||||
.oauth2ResourceServer {
|
.oauth2ResourceServer {
|
||||||
it.jwt(Customizer.withDefaults())
|
it.jwt(Customizer.withDefaults())
|
||||||
}
|
}
|
||||||
|
.passwordManagement { }
|
||||||
.formLogin(Customizer.withDefaults())
|
.formLogin(Customizer.withDefaults())
|
||||||
.csrf {
|
.csrf {
|
||||||
it.ignoringRequestMatchers(builder.pattern("/api/**"))
|
it.ignoringRequestMatchers(builder.pattern("/api/**"))
|
||||||
|
|
|
@ -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
|
||||||
|
"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||||
|
<Link rel="lrdd" type="application/xrd+xml"
|
||||||
|
template="${applicationConfig.url}/.well-known/webfinger?resource={uri}"/>
|
||||||
|
</XRD>"""
|
||||||
|
|
||||||
|
@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<String> {
|
||||||
|
return ResponseEntity(xml, HttpStatus.OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/.well-known/host-meta.json", produces = ["application/json"])
|
||||||
|
fun hostmetJson(): ResponseEntity<String> {
|
||||||
|
return ResponseEntity(json, HttpStatus.OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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<Nodeinfo> {
|
||||||
|
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<Nodeinfo2_0> {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<WebFinger> = 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package dev.usbharu.hideout.domain.model.wellknown
|
||||||
|
|
||||||
|
|
||||||
|
data class Nodeinfo(
|
||||||
|
val links: List<Links>
|
||||||
|
) {
|
||||||
|
data class Links(
|
||||||
|
val rel: String,
|
||||||
|
val href: String
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package dev.usbharu.hideout.domain.model.wellknown
|
||||||
|
|
||||||
|
data class Nodeinfo2_0(
|
||||||
|
val version: String,
|
||||||
|
val software: Software,
|
||||||
|
val protocols: List<String>,
|
||||||
|
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<String>,
|
||||||
|
val outbound: List<String>
|
||||||
|
)
|
||||||
|
|
||||||
|
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<String>,
|
||||||
|
val tosUrl: String,
|
||||||
|
val repositoryUrl: String,
|
||||||
|
val feedbackUrl: String,
|
||||||
|
) {
|
||||||
|
data class Maintainer(
|
||||||
|
val name: String,
|
||||||
|
val email: String
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue