go-ssb-room/web/handlers/aliases.go

181 lines
4.8 KiB
Go
Raw Normal View History

// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
//
// SPDX-License-Identifier: MIT
2021-03-15 11:25:07 +00:00
package handlers
import (
"encoding/base64"
"encoding/json"
"fmt"
2021-03-22 15:01:45 +00:00
"html/template"
2021-03-15 11:25:07 +00:00
"log"
"net/http"
2021-03-22 15:01:45 +00:00
"net/url"
2021-03-15 11:25:07 +00:00
"github.com/gorilla/mux"
"go.mindeco.de/http/render"
"github.com/ssbc/go-ssb-room/v2/internal/aliases"
"github.com/ssbc/go-ssb-room/v2/internal/network"
"github.com/ssbc/go-ssb-room/v2/roomdb"
"github.com/ssbc/go-ssb-room/v2/web"
2021-03-15 11:25:07 +00:00
)
2021-03-16 08:10:44 +00:00
// aliasHandler implements the public resolve endpoint for HTML and JSON requests.
2021-03-15 11:25:07 +00:00
type aliasHandler struct {
r *render.Renderer
db roomdb.AliasesService
config roomdb.RoomConfig
2021-03-15 11:25:07 +00:00
roomEndpoint network.ServerEndpointDetails
2021-03-15 11:25:07 +00:00
}
func (h aliasHandler) resolve(rw http.ResponseWriter, req *http.Request) {
2021-03-15 11:25:07 +00:00
respEncoding := req.URL.Query().Get("encoding")
var ar aliasResponder
switch respEncoding {
case "json":
ar = newAliasJSONResponder(rw)
default:
ar = newAliasHTMLResponder(h.r, rw, req)
2021-03-15 11:25:07 +00:00
}
ar.UpdateRoomInfo(h.roomEndpoint)
2021-03-15 11:25:07 +00:00
pm, err := h.config.GetPrivacyMode(req.Context())
if err != nil {
ar.SendError(fmt.Errorf("room is running an unknown privacy mode"))
return
}
if pm == roomdb.ModeRestricted {
ar.SendError(fmt.Errorf("this room is restricted, alias resolving is turned off"))
return
}
2021-03-15 11:25:07 +00:00
name := mux.Vars(req)["alias"]
if name == "" && !aliases.IsValid(name) {
ar.SendError(fmt.Errorf("invalid alias"))
return
}
alias, err := h.db.Resolve(req.Context(), name)
2021-03-15 11:25:07 +00:00
if err != nil {
ar.SendError(fmt.Errorf("aliases: failed to resolve name %q: %w", name, err))
2021-03-15 11:25:07 +00:00
return
}
ar.SendConfirmation(alias)
}
// aliasResponder is supposed to handle different encoding types transparently.
2021-03-15 11:25:07 +00:00
// It either sends the signed alias confirmation or an error.
type aliasResponder interface {
SendConfirmation(roomdb.Alias)
SendError(error)
UpdateRoomInfo(netInfo network.ServerEndpointDetails)
2021-03-15 11:25:07 +00:00
}
// aliasJSONResponse dictates the field names and format of the JSON response for the alias web endpoint
type aliasJSONResponse struct {
Status string `json:"status"`
MultiserverAddress string `json:"multiserverAddress"`
RoomID string `json:"roomId"`
UserID string `json:"userId"`
Alias string `json:"alias"`
Signature string `json:"signature"`
2021-03-15 11:25:07 +00:00
}
// handles JSON responses
type aliasJSONResponder struct {
enc *json.Encoder
netInfo network.ServerEndpointDetails
2021-03-15 11:25:07 +00:00
}
func newAliasJSONResponder(rw http.ResponseWriter) aliasResponder {
2021-03-23 07:09:04 +00:00
rw.Header().Set("Content-Type", "application/json")
2021-03-15 11:25:07 +00:00
return &aliasJSONResponder{
enc: json.NewEncoder(rw),
}
}
func (json *aliasJSONResponder) UpdateRoomInfo(netInfo network.ServerEndpointDetails) {
json.netInfo = netInfo
2021-03-15 11:25:07 +00:00
}
func (json aliasJSONResponder) SendConfirmation(alias roomdb.Alias) {
var resp = aliasJSONResponse{
Status: "successful",
RoomID: json.netInfo.RoomID.String(),
MultiserverAddress: json.netInfo.MultiserverAddress(),
Alias: alias.Name,
UserID: alias.Feed.String(),
Signature: base64.StdEncoding.EncodeToString(alias.Signature),
2021-03-15 11:25:07 +00:00
}
json.enc.Encode(resp)
}
func (json aliasJSONResponder) SendError(err error) {
json.enc.Encode(struct {
Status string `json:"status"`
Error string `json:"error"`
}{"error", err.Error()})
}
// handles HTML responses
type aliasHTMLResponder struct {
renderer *render.Renderer
rw http.ResponseWriter
req *http.Request
netInfo network.ServerEndpointDetails
2021-03-15 11:25:07 +00:00
}
func newAliasHTMLResponder(r *render.Renderer, rw http.ResponseWriter, req *http.Request) aliasResponder {
return &aliasHTMLResponder{
renderer: r,
rw: rw,
req: req,
}
}
func (html *aliasHTMLResponder) UpdateRoomInfo(netInfo network.ServerEndpointDetails) {
html.netInfo = netInfo
2021-03-15 11:25:07 +00:00
}
func (html aliasHTMLResponder) SendConfirmation(alias roomdb.Alias) {
2021-03-22 15:01:45 +00:00
// construct the ssb:experimental?action=consume-alias&... uri for linking into apps
queryParams := url.Values{}
queryParams.Set("action", "consume-alias")
queryParams.Set("roomId", html.netInfo.RoomID.String())
2021-03-22 15:01:45 +00:00
queryParams.Set("alias", alias.Name)
queryParams.Set("userId", alias.Feed.String())
2021-03-22 15:01:45 +00:00
queryParams.Set("signature", base64.URLEncoding.EncodeToString(alias.Signature))
queryParams.Set("multiserverAddress", html.netInfo.MultiserverAddress())
2021-03-22 15:01:45 +00:00
// html.multiservAddr
ssbURI := url.URL{
Scheme: "ssb",
Opaque: "experimental",
2021-03-22 15:01:45 +00:00
RawQuery: queryParams.Encode(),
}
err := html.renderer.Render(html.rw, html.req, "alias.tmpl", http.StatusOK, struct {
2021-03-15 11:25:07 +00:00
Alias roomdb.Alias
2021-03-22 15:01:45 +00:00
SSBURI template.URL
2021-11-12 11:34:35 +00:00
}{alias, template.URL(web.StringifySSBURI(&ssbURI, html.req.UserAgent()))})
2021-03-15 11:25:07 +00:00
if err != nil {
log.Println("alias-resolve render errr:", err)
}
}
func (html aliasHTMLResponder) SendError(err error) {
html.renderer.Error(html.rw, html.req, http.StatusInternalServerError, err)
}