mirror of
https://git.mills.io/saltyim/saltyim.git
synced 2024-07-03 00:33:38 +00:00
![James Mills](/assets/img/avatar_default.png)
Alternative to #177 The way this works is: Client: - Client creates a normal `net/http.Request{}` object using the `Request()` function in `utils.go`. The `http.Request{}` object is then signed using the Client's Ed25519 private key. - The HTTP Method and Path (_note this is important_) are hashed, as well as the request body (if any) using the FNV128a hashing algorithm. - This hash is then signed by the Client's's Ed25519 private key. - The resulting signature is then encoded to Base64 (_standard encoding_) and added to the HTTP headers as a `Signature:` header. - In addition the Client's Ed25519 public key is added to the HTTP headers as `Signer:` Server: - The server calculates the same FNV128a hash of the HTTP Request Method and Path and the body (if any) - The server decodes the HTTP header `Signature:` - The server then uses the Client's Ed25519 public key in the HTTP header `Signer:` to verify the signature of the `Signature:` HTTP header which gives us back the original FNV128a hash the Client calculated for the request. - The server then compares the Client's hash with the expected hash to see if they compare equally. Co-authored-by: James Mills <1290234+prologic@users.noreply.github.com> Co-authored-by: Jon Lundy <jon@xuu.cc> Reviewed-on: https://git.mills.io/saltyim/saltyim/pulls/178 Reviewed-by: xuu <xuu@noreply@mills.io>
188 lines
5.4 KiB
Go
188 lines
5.4 KiB
Go
package components
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/maxence-charriere/go-app/v9/pkg/app"
|
|
"github.com/mlctrez/goapp-mdc/pkg/base"
|
|
"github.com/mlctrez/goapp-mdc/pkg/button"
|
|
"github.com/mlctrez/goapp-mdc/pkg/icon"
|
|
"github.com/mlctrez/goapp-mdc/pkg/textarea"
|
|
"github.com/mlctrez/goapp-mdc/pkg/textfield"
|
|
"go.mills.io/saltyim"
|
|
"go.mills.io/saltyim/internal/pwa/storage"
|
|
)
|
|
|
|
type Configuration struct {
|
|
app.Compo
|
|
base.JsUtil
|
|
|
|
// to support removal
|
|
Contacts []string
|
|
|
|
dialog *ModalDialog
|
|
|
|
// components
|
|
user *textfield.TextField
|
|
identity *textarea.TextArea
|
|
}
|
|
|
|
func (c *Configuration) readState(ctx app.Context) {
|
|
|
|
identity, err := GetIdentityFromState(ctx)
|
|
if err != nil {
|
|
// TODO: display dialog
|
|
log.Println("state read error", err)
|
|
return
|
|
}
|
|
c.identity.Value = string(identity.Contents())
|
|
c.user.Value = identity.Addr().String()
|
|
c.Contacts = storage.ContactsLocalStorage(ctx).List()
|
|
}
|
|
|
|
func (c *Configuration) OnMount(ctx app.Context) {
|
|
if app.IsClient {
|
|
c.readState(ctx)
|
|
}
|
|
}
|
|
|
|
func (c *Configuration) Render() app.UI {
|
|
if c.user == nil {
|
|
c.user = &textfield.TextField{Id: "config-user", Label: "User in the form user@domain"}
|
|
c.identity = textarea.New("identity").Size(5, 100).Label("identity").MaxLength(1024)
|
|
c.identity.WithCallback(func(in app.HTMLTextarea) {
|
|
in.OnChange(c.identity.ValueTo(&c.identity.Value))
|
|
})
|
|
c.dialog = &ModalDialog{}
|
|
}
|
|
|
|
return PageBody(
|
|
app.Div().ID("wrapper").Body(
|
|
app.H4().Text("configuration"),
|
|
c.dialog,
|
|
c.user,
|
|
&button.Button{Icon: string(icon.MICreate), Label: "new identity",
|
|
Outlined: true, Raised: true, Callback: c.newIdentity()},
|
|
app.Br(),
|
|
c.identity,
|
|
&button.Button{Icon: string(icon.MIUpdate), Label: "update identity",
|
|
Outlined: true, Raised: true, Callback: c.updateIdentity()},
|
|
app.Br(),
|
|
app.H4().Text("register with salty@domain for above identity"),
|
|
&button.Button{Icon: string(icon.MICreate), Label: "register",
|
|
Outlined: true, Raised: true, Callback: c.registerIdentity()},
|
|
app.Hr(),
|
|
app.H4().Text("remove contacts and storage"),
|
|
c.buildDeleteContacts(),
|
|
),
|
|
)
|
|
}
|
|
|
|
func (c *Configuration) buildDeleteContacts() app.UI {
|
|
return app.Range(c.Contacts).Slice(func(i int) app.UI {
|
|
return app.Div().Body(
|
|
&button.Button{
|
|
Id: fmt.Sprintf("%x", sha256.Sum256([]byte(c.Contacts[i]))),
|
|
Icon: string(icon.MIDelete),
|
|
Label: c.Contacts[i],
|
|
Raised: true, Outlined: true,
|
|
Callback: func(button app.HTMLButton) {
|
|
button.OnClick(func(ctx app.Context, e app.Event) {
|
|
storage.ConversationsLocalStorage(ctx, c.Contacts[i]).Delete()
|
|
storage.ContactsLocalStorage(ctx).Remove(c.Contacts[i])
|
|
c.Contacts = storage.ContactsLocalStorage(ctx).List()
|
|
NavigationUpdate(ctx)
|
|
ctx.Defer(func(context app.Context) {
|
|
c.Update()
|
|
})
|
|
})
|
|
},
|
|
},
|
|
app.Br(),
|
|
)
|
|
})
|
|
}
|
|
|
|
func (c *Configuration) registerIdentity() func(button app.HTMLButton) {
|
|
return func(button app.HTMLButton) {
|
|
button.OnClick(func(ctx app.Context, e app.Event) {
|
|
identity, err := GetIdentityFromState(ctx)
|
|
if err != nil { // TODO: pop dialog
|
|
log.Println("error", err)
|
|
return
|
|
}
|
|
// not using client since that's not setup until we have an identity, might break the existing
|
|
// flow
|
|
log.Printf("identity;Addr(): %#v", identity.Addr())
|
|
registerClient, err := saltyim.NewClient(saltyim.WithClientIdentity(saltyim.WithIdentityBytes(identity.Contents())))
|
|
if err != nil { // TODO: pop dialog
|
|
log.Println("error", err)
|
|
return
|
|
}
|
|
err = registerClient.Register(app.Getenv("BrokerURI"))
|
|
if err != nil { // TODO: pop dialog
|
|
log.Println("error", err)
|
|
c.dialog.ShowError("error registering to broker: ", err.Error())
|
|
return
|
|
}
|
|
c.dialog.ShowDialog("Success", "🥳 Success!")
|
|
})
|
|
}
|
|
}
|
|
|
|
func (c *Configuration) newIdentity() func(button app.HTMLButton) {
|
|
return func(button app.HTMLButton) {
|
|
button.OnClick(func(ctx app.Context, e app.Event) {
|
|
addr, err := saltyim.ParseAddr(c.user.Value)
|
|
if err != nil { // TODO: pop dialog
|
|
log.Println("error", err)
|
|
c.dialog.ShowError("error parsing address: ", err.Error())
|
|
return
|
|
}
|
|
identity, err := saltyim.CreateIdentity(saltyim.WithIdentityAddr(addr))
|
|
if err != nil { // TODO: pop dialog
|
|
log.Println("error", err)
|
|
c.dialog.ShowError("error creating identity: ", err.Error())
|
|
return
|
|
}
|
|
c.identity.Value = string(identity.Contents())
|
|
err = SetIdentityToState(ctx, identity)
|
|
if err != nil { // TODO: pop dialog
|
|
log.Println("error", err)
|
|
c.dialog.ShowError("error saving identity to storage: ", err.Error())
|
|
return
|
|
}
|
|
c.identity.Update()
|
|
})
|
|
}
|
|
}
|
|
|
|
func (c *Configuration) updateIdentity() func(button app.HTMLButton) {
|
|
return func(button app.HTMLButton) {
|
|
button.OnClick(func(ctx app.Context, e app.Event) {
|
|
identityString := c.identity.Value
|
|
identity, err := saltyim.GetIdentity(saltyim.WithIdentityBytes([]byte(identityString)))
|
|
if err != nil { // TODO: pop dialog
|
|
log.Println("error", err)
|
|
return
|
|
}
|
|
err = SetIdentityToState(ctx, identity)
|
|
if err != nil { // TODO: pop dialog
|
|
log.Println("error", err)
|
|
return
|
|
}
|
|
c.user.Value = identity.Addr().String()
|
|
c.user.Update()
|
|
})
|
|
}
|
|
}
|
|
|
|
func (c *Configuration) topActions() (actions []app.HTMLButton) {
|
|
actions = append(actions, icon.MIRefresh.Button().Title("reload").
|
|
OnClick(func(ctx app.Context, e app.Event) { ctx.Reload() }))
|
|
|
|
return actions
|
|
}
|