// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021 // // SPDX-License-Identifier: MIT // Package alias implements the muxrpc handlers for alias needs. package alias import ( "context" "encoding/base64" "encoding/json" "errors" "fmt" "strings" "github.com/ssbc/go-muxrpc/v2" kitlog "go.mindeco.de/log" refs "github.com/ssbc/go-ssb-refs" "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/router" ) // Handler implements the muxrpc methods for alias registration and recvocation type Handler struct { logger kitlog.Logger self refs.FeedRef db roomdb.AliasesService netInfo network.ServerEndpointDetails // roomDomain string // the http(s) domain of the room to signal alias addresses } // New returns a fresh alias muxrpc handler func New(log kitlog.Logger, self refs.FeedRef, aliasesDB roomdb.AliasesService, netInfo network.ServerEndpointDetails) Handler { var h Handler h.self = self h.netInfo = netInfo h.logger = log h.db = aliasesDB return h } const sigSuffix = ".sig.ed25519" var httpRouter = router.CompleteApp() // Register is an async muxrpc method handler for registering aliases. // It receives two string arguments over muxrpc (alias and signature), // checks the signature confirmation is correct (for this room and signed by the key of theconnection) // If it is valid, it registers the alias on the roomdb and returns true. If not it returns an error. func (h Handler) Register(ctx context.Context, req *muxrpc.Request) (interface{}, error) { var args []string err := json.Unmarshal(req.RawArgs, &args) if err != nil { return nil, fmt.Errorf("registerAlias: bad request: %w", err) } if n := len(args); n != 2 { return nil, fmt.Errorf("registerAlias: expected two arguments got %d", n) } if !strings.HasSuffix(args[1], sigSuffix) { return nil, fmt.Errorf("registerAlias: signature does not have the expected suffix") } // remove the suffix of the base64 string sig := strings.TrimSuffix(args[1], sigSuffix) var confirmation aliases.Confirmation confirmation.RoomID = h.self confirmation.Alias = args[0] confirmation.Signature, err = base64.StdEncoding.DecodeString(sig) if err != nil { return nil, fmt.Errorf("registerAlias: bad signature encoding: %w", err) } // check alias is valid if !aliases.IsValid(confirmation.Alias) { return nil, fmt.Errorf("registerAlias: invalid alias") } // get the user from the muxrpc connection userID, err := network.GetFeedRefFromAddr(req.RemoteAddr()) if err != nil { return nil, err } confirmation.UserID = userID // check the signature if !confirmation.Verify() { return nil, fmt.Errorf("registerAlias: invalid signature") } err = h.db.Register(ctx, confirmation.Alias, confirmation.UserID, confirmation.Signature) if err != nil { var takenErr roomdb.ErrAliasTaken if errors.As(err, &takenErr) { return nil, takenErr } return nil, fmt.Errorf("registerAlias: could not register alias: %w", err) } return h.netInfo.URLForAlias(confirmation.Alias), nil } // Revoke checks that the alias is from that user before revoking the alias from the database. func (h Handler) Revoke(ctx context.Context, req *muxrpc.Request) (interface{}, error) { var args []string err := json.Unmarshal(req.RawArgs, &args) if err != nil { return nil, fmt.Errorf("registerAlias: bad request: %w", err) } if n := len(args); n != 1 { return nil, fmt.Errorf("registerAlias: expected two arguments got %d", n) } // get the user from the muxrpc connection userID, err := network.GetFeedRefFromAddr(req.RemoteAddr()) if err != nil { return nil, err } alias, err := h.db.Resolve(ctx, args[0]) if err != nil { return nil, err } if !alias.Feed.Equal(userID) { return nil, fmt.Errorf("revokeAlias: not your alias (moderators need to use the web dashboard of the room") } err = h.db.Revoke(ctx, alias.Name) if err != nil { return nil, err } return true, nil } func (h Handler) List(ctx context.Context, req *muxrpc.Request) (interface{}, error) { var args []string err := json.Unmarshal(req.RawArgs, &args) if err != nil { return nil, fmt.Errorf("listAlias: bad request: %w", err) } if n := len(args); n != 1 { return nil, fmt.Errorf("listAlias: expected one argument got %d", n) } ref, err := refs.ParseFeedRef(args[0]) if err != nil { return nil, fmt.Errorf("listAlias: invalid feed ref: %w", err) } allAliases, err := h.db.List(ctx) if err != nil { return nil, fmt.Errorf("listAlias: could not list aliases: %w", err) } var filteredAliases []roomdb.Alias for _, alias := range allAliases { if alias.Feed.Equal(ref) { filteredAliases = append(filteredAliases, alias) } } return aliasesToListOfAliasStrings(filteredAliases), nil } func aliasesToListOfAliasStrings(aliases []roomdb.Alias) []string { result := make([]string, 0) for _, alias := range aliases { result = append(result, alias.Name) } return result }