2024-01-19 10:21:31 +00:00
|
|
|
// Copyright (c) 2024 mStar
|
|
|
|
//
|
|
|
|
// Licensed under the EUPL, Version 1.2
|
|
|
|
//
|
|
|
|
// You may not use this work except in compliance with the Licence.
|
|
|
|
// You should have received a copy of the Licence along with this work. If not, see:
|
|
|
|
// <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>.
|
|
|
|
// See the Licence for the specific language governing permissions and limitations under the Licence.
|
|
|
|
//
|
|
|
|
|
|
|
|
package ap
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
"gitlab.com/mstarongitlab/goutils/other"
|
|
|
|
"gitlab.com/mstarongitlab/linstrom/server"
|
2024-01-29 17:08:52 +00:00
|
|
|
"gitlab.com/mstarongitlab/linstrom/storage"
|
2024-01-19 10:21:31 +00:00
|
|
|
"gitlab.com/mstarongitlab/linstrom/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ErrInvalidResource = errors.New("invalid resource")
|
|
|
|
|
|
|
|
type webfingerLink struct {
|
|
|
|
Rel string `json:"self"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
Target url.URL `json:"href"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type WebfingerResponse struct {
|
|
|
|
Subject string `json:"subject"`
|
|
|
|
Links []webfingerLink `json:"links"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func WebfingerEndpoint(server *server.Server) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
|
|
|
r.ParseForm()
|
|
|
|
req := r.FormValue("resource")
|
|
|
|
if req == "" {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
acc, err := getAccountFromWebfingerResource(req)
|
|
|
|
if errors.Is(err, ErrInvalidResource) {
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if acc.Host.Hostname() != server.Config.General.Domain {
|
|
|
|
http.Error(w, fmt.Sprintf("wrong host, try %s", &acc.Host), http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-29 17:08:52 +00:00
|
|
|
person, err := server.Storage.GetPersonByName(acc.Name)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, storage.ErrNotFound) {
|
|
|
|
http.Error(w, fmt.Sprintf("Account %s not found", acc), http.StatusNotFound)
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
http.Error(w, fmt.Sprintf("Internal error: %s, please report", err.Error()), http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-19 10:21:31 +00:00
|
|
|
response := WebfingerResponse{
|
|
|
|
Links: []webfingerLink{
|
|
|
|
{
|
2024-01-29 17:08:52 +00:00
|
|
|
Rel: "me",
|
|
|
|
Type: "application/activity+json",
|
|
|
|
Target: *other.Must(url.Parse(fmt.Sprintf(
|
|
|
|
"https://%s/api/ap/user/%s",
|
|
|
|
server.Config.General.Domain,
|
|
|
|
person.Uid,
|
|
|
|
))),
|
2024-01-19 10:21:31 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
json.Marshal(response)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-29 17:08:52 +00:00
|
|
|
// turns "acct:bugle@bugle.lol" into { Name: bugle, Host: bugle.lol }
|
2024-01-19 10:21:31 +00:00
|
|
|
func getAccountFromWebfingerResource(r string) (*types.AccountHandle, error) {
|
|
|
|
acctCheck, acc, found := strings.Cut(r, ":")
|
|
|
|
if !found || acctCheck != "acct" {
|
|
|
|
return nil, ErrInvalidResource
|
|
|
|
}
|
|
|
|
name, rawHost, found := strings.Cut(acc, "@")
|
|
|
|
if !found {
|
|
|
|
return nil, ErrInvalidResource
|
|
|
|
}
|
|
|
|
host, err := url.Parse(rawHost)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("%w: Invalid url: %w", ErrInvalidResource, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &types.AccountHandle{
|
|
|
|
Name: name,
|
|
|
|
Host: *host,
|
|
|
|
}, nil
|
|
|
|
}
|