Compare commits
No commits in common. "7eac1db4758510d2db6ba0d8d8c85db6784e31f6" and "02c2e53c5157d497564b529651b8a57658097688" have entirely different histories.
7eac1db475
...
02c2e53c51
10 changed files with 22 additions and 202 deletions
|
@ -77,7 +77,7 @@ func ImportRemoteAccount(targetName string) (string, error) {
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, _ := io.ReadAll(response.Body)
|
body, _ := io.ReadAll(response.Body)
|
||||||
log.Debug().
|
log.Trace().
|
||||||
Int("status", response.StatusCode).
|
Int("status", response.StatusCode).
|
||||||
Bytes("body", body).
|
Bytes("body", body).
|
||||||
Any("headers", response.Header).
|
Any("headers", response.Header).
|
||||||
|
@ -394,7 +394,7 @@ func ImportRemoteServer(host string) (uint, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if nodeAdmins, ok := data.Metadata["nodeAdmins"].([]map[string]any); ok && len(nodeAdmins) > 0 {
|
if nodeAdmins, ok := data.Metadata["nodeAdmins"].([]map[string]any); ok {
|
||||||
log.Debug().Msg("Node admins url found")
|
log.Debug().Msg("Node admins url found")
|
||||||
targets := sliceutils.Filter(
|
targets := sliceutils.Filter(
|
||||||
existingEntry.Metadata,
|
existingEntry.Metadata,
|
||||||
|
@ -530,7 +530,7 @@ func ImportRemoteServer(host string) (uint, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if staffAccounts, ok := data.Metadata["nodeAdmins"].([]any); ok && len(staffAccounts) > 0 {
|
if staffAccounts, ok := data.Metadata["nodeAdmins"].([]any); ok {
|
||||||
log.Debug().Msg("Node admins url found")
|
log.Debug().Msg("Node admins url found")
|
||||||
targets := sliceutils.Filter(
|
targets := sliceutils.Filter(
|
||||||
existingEntry.Metadata,
|
existingEntry.Metadata,
|
||||||
|
|
|
@ -8,6 +8,5 @@ const (
|
||||||
// where multiple releases in a day are required
|
// where multiple releases in a day are required
|
||||||
Version = "0.0.1 pre-alpha"
|
Version = "0.0.1 pre-alpha"
|
||||||
// Username for the server actor
|
// Username for the server actor
|
||||||
ServerActorName = "server.actor"
|
ServerActorName = "server.actor"
|
||||||
FeedUsernameSuffix = "-feed"
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package shared
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
|
||||||
)
|
|
||||||
|
|
||||||
var forbiddenUsernames = []string{
|
|
||||||
"server.actor",
|
|
||||||
"feed",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reports whether a given user name is valid (for non-internal systems)
|
|
||||||
//
|
|
||||||
// TODO: Include compat check for Mastodon?
|
|
||||||
func ValidateUsername(username string) bool {
|
|
||||||
if strings.HasSuffix(username, FeedUsernameSuffix) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if sliceutils.Contains(forbiddenUsernames, username) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
|
|
||||||
"git.mstar.dev/mstar/goutils/other"
|
"git.mstar.dev/mstar/goutils/other"
|
||||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"git.mstar.dev/mstar/linstrom/storage-new/models"
|
"git.mstar.dev/mstar/linstrom/storage-new/models"
|
||||||
|
@ -14,11 +13,6 @@ import (
|
||||||
|
|
||||||
// Auto-migrate all tables and types used
|
// Auto-migrate all tables and types used
|
||||||
func Migrate(db *gorm.DB) error {
|
func Migrate(db *gorm.DB) error {
|
||||||
// Shut up gorm's queries during automigrate by setting log level to info during migration
|
|
||||||
// and then back to the previous value on exit
|
|
||||||
currentLogLevel := zerolog.GlobalLevel()
|
|
||||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
|
||||||
defer zerolog.SetGlobalLevel(currentLogLevel)
|
|
||||||
if err := createAccountAuthMethodType(db); err != nil {
|
if err := createAccountAuthMethodType(db); err != nil {
|
||||||
return other.Error("storage", "Failed to create Auth Method type", err)
|
return other.Error("storage", "Failed to create Auth Method type", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,27 +169,21 @@ func insertUserPronoun(user *models.User) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func attachUserToRole(user *models.User) error {
|
func attachUserToRole(user *models.User) error {
|
||||||
roles := []models.Role{models.DefaultUserRole, models.ServerActorRole, models.FullAdminRole}
|
_, err := dbgen.UserToRole.Where(dbgen.UserToRole.UserId.Eq(user.ID)).
|
||||||
for _, role := range roles {
|
Where(dbgen.UserToRole.RoleId.Eq(models.FullAdminRole.ID)).
|
||||||
_, err := dbgen.UserToRole.Where(dbgen.UserToRole.UserId.Eq(user.ID)).
|
First()
|
||||||
Where(dbgen.UserToRole.RoleId.Eq(role.ID)).
|
switch err {
|
||||||
First()
|
case nil:
|
||||||
switch err {
|
return nil
|
||||||
case nil:
|
case gorm.ErrRecordNotFound:
|
||||||
continue
|
u2r := models.UserToRole{
|
||||||
case gorm.ErrRecordNotFound:
|
User: *user,
|
||||||
u2r := models.UserToRole{
|
UserId: user.ID,
|
||||||
User: *user,
|
Role: models.FullAdminRole,
|
||||||
UserId: user.ID,
|
RoleId: models.FullAdminRole.ID,
|
||||||
Role: role,
|
|
||||||
RoleId: role.ID,
|
|
||||||
}
|
|
||||||
if err = dbgen.UserToRole.Save(&u2r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
return dbgen.UserToRole.Save(&u2r)
|
||||||
|
default:
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[general]
|
[general]
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
domain = "lhr.life"
|
domain = "lhr.life"
|
||||||
subdomain = "3e4af10addc7d0"
|
subdomain = "47565bb39100de"
|
||||||
private_port = 8080
|
private_port = 8080
|
||||||
public_port = 443
|
public_port = 443
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
webshared "git.mstar.dev/mstar/linstrom/web/shared"
|
webshared "git.mstar.dev/mstar/linstrom/web/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
var webfingerResourceRegex = regexp.MustCompile(`acct:(?P<username>[\w\.-]+)@(?<domain>[\w\.-]+)`)
|
var webfingerResourceRegex = regexp.MustCompile(`acct:(?P<username>[\w-]+)@(?<domain>[\w\.-]+)`)
|
||||||
|
|
||||||
func WellKnownWebfinger(w http.ResponseWriter, r *http.Request) {
|
func WellKnownWebfinger(w http.ResponseWriter, r *http.Request) {
|
||||||
type OutboundLink struct {
|
type OutboundLink struct {
|
||||||
|
|
|
@ -12,14 +12,12 @@ var errorDescriptions = map[string]string{
|
||||||
"db-failure": "The database query for this request failed for an undisclosed reason. This is often caused by bad input data conflicting with existing information. Try to submit different data or wait for some time",
|
"db-failure": "The database query for this request failed for an undisclosed reason. This is often caused by bad input data conflicting with existing information. Try to submit different data or wait for some time",
|
||||||
"bad-request-data": "The data provided in the request doesn't match the requirements, see problem details' detail field for more information",
|
"bad-request-data": "The data provided in the request doesn't match the requirements, see problem details' detail field for more information",
|
||||||
"bad-page": "The provided page number was not valid. See response details for more information",
|
"bad-page": "The provided page number was not valid. See response details for more information",
|
||||||
"bad-accept-mime-type": "The requested mime type is not supported for the target url",
|
|
||||||
"invalid-auth-signature": "The requested URL requires verification via authorized fetch. See https://swicg.github.io/activitypub-http-signature/ and https://datatracker.ietf.org/doc/html/rfc9421 (not implemented yet) for details on how to verify your request",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func errorTypeHandler(w http.ResponseWriter, r *http.Request) {
|
func errorTypeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
errName := r.PathValue("name")
|
errName := r.PathValue("name")
|
||||||
if description, ok := errorDescriptions[errName]; ok {
|
if description, ok := errorDescriptions[errName]; ok {
|
||||||
_, _ = fmt.Fprint(w, description)
|
fmt.Fprint(w, description)
|
||||||
} else {
|
} else {
|
||||||
webutils.ProblemDetailsStatusOnly(w, 404)
|
webutils.ProblemDetailsStatusOnly(w, 404)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,104 +0,0 @@
|
||||||
package webmiddleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
webutils "git.mstar.dev/mstar/goutils/http"
|
|
||||||
"git.mstar.dev/mstar/goutils/other"
|
|
||||||
)
|
|
||||||
|
|
||||||
const signatureRegexString = `keyId=([a-zA-Z0-9_\-\.:@/\?&=#%\+\[\]!$\(\)\*,;]+),headers="([a-z0-9-_\(\) ]+)",(?:algorithm="([a-z0-9-])+",)?signature="(.+)"`
|
|
||||||
|
|
||||||
var publicPaths = []*regexp.Regexp{
|
|
||||||
regexp.MustCompile(`/\.well-known/.+^`),
|
|
||||||
// regexp.MustCompile(``),
|
|
||||||
}
|
|
||||||
|
|
||||||
var signatureRegex = regexp.MustCompile(signatureRegexString)
|
|
||||||
|
|
||||||
// Builder for the authorized fetch check middleware.
|
|
||||||
// forNonGet determines whether the check should happen for non-GET requests.
|
|
||||||
// forGet determines whether the check should also happen for GET requests.
|
|
||||||
// forGet being true implicitly sets forNonGet true too.
|
|
||||||
// Requests to the server actor and other, required public resources
|
|
||||||
// will never be checked
|
|
||||||
func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuilder {
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
path := r.URL.Path
|
|
||||||
// Check always open path first
|
|
||||||
for _, re := range publicPaths {
|
|
||||||
if re.Match([]byte(path)) {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Not an always open path, check methods
|
|
||||||
if r.Method == "GET" && !forGet {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
} else if !forGet && !forNonGet {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO: Perform check here
|
|
||||||
signatureHeader := r.Header.Get("Signature")
|
|
||||||
if signatureHeader == "" {
|
|
||||||
webutils.ProblemDetails(
|
|
||||||
w,
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
"/errors/invalid-auth-signature",
|
|
||||||
"invalid authorization signature",
|
|
||||||
other.IntoPointer("Missing Signature header for authorization"),
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
match := signatureRegex.FindStringSubmatch(signatureHeader)
|
|
||||||
if len(match) <= 1 {
|
|
||||||
webutils.ProblemDetails(
|
|
||||||
w,
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
"/errors/invalid-auth-signature",
|
|
||||||
"invalid authorization signature",
|
|
||||||
other.IntoPointer("Invalid signature header format"),
|
|
||||||
map[string]any{
|
|
||||||
"used signature regex": signatureRegexString,
|
|
||||||
"signature format": `keyId="<url to actor with used key>",headers="<list of headers used, including pseudo header (request-target)>",(optional: algorithm="<one of rsa-sha256, hs2019>", (defaults to rsa-sha256 if not set))signature="signed message"`,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// fullMatch = match[0]
|
|
||||||
rawKeyId := match[1]
|
|
||||||
rawHeaders := match[2]
|
|
||||||
rawAlgorithm := match[3]
|
|
||||||
signature := match[4]
|
|
||||||
|
|
||||||
_, err := url.Parse(rawKeyId)
|
|
||||||
if err != nil {
|
|
||||||
webutils.ProblemDetails(
|
|
||||||
w,
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
"/errors/invalid-auth-signature",
|
|
||||||
"invalid authorization signature",
|
|
||||||
other.IntoPointer("keyId must be a valid url"),
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawAlgorithm == "" {
|
|
||||||
rawAlgorithm = "hs2019"
|
|
||||||
// w.Header().Add("X-Algorithm-Hint", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = rawHeaders
|
|
||||||
_ = signature
|
|
||||||
_ = rawAlgorithm
|
|
||||||
panic("not implemented")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package webmiddleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
webutils "git.mstar.dev/mstar/goutils/http"
|
|
||||||
"git.mstar.dev/mstar/goutils/other"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BuildMimetypeCheck(acceptedMimetypes []string) webutils.HandlerBuilder {
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
acceptHeader := r.Header.Get("Accept")
|
|
||||||
for _, couldBe := range acceptedMimetypes {
|
|
||||||
if strings.Contains(acceptHeader, couldBe) {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Requested mimtype not in list of accepted ones, failing request with note
|
|
||||||
webutils.ProblemDetails(
|
|
||||||
w,
|
|
||||||
http.StatusUnsupportedMediaType,
|
|
||||||
"/errors/bad-accept-mime-type",
|
|
||||||
"unsupported mime type",
|
|
||||||
other.IntoPointer(
|
|
||||||
"The requested mime type is not supported. Please check extras for a list of supported mime types",
|
|
||||||
),
|
|
||||||
map[string]any{
|
|
||||||
"supported-mime-types": acceptedMimetypes,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue