Skip to content

Commit

Permalink
Implement pinentry for mac and windows
Browse files Browse the repository at this point in the history
  • Loading branch information
quexten committed Sep 20, 2023
1 parent 75c7a42 commit d09cc37
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 9 deletions.
17 changes: 17 additions & 0 deletions agent/defaultmainloop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//go:build !windows && !darwin && !linux

package agent

import (
"fmt"
"os"
"os/signal"
"syscall"
)

func mainloop() {
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
fmt.Println("Blocking, press ctrl+c to continue...")
<-done
}
9 changes: 9 additions & 0 deletions agent/giouimainloop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build windows || darwin || linux

package agent

import "gioui.org/app"

func mainloop() {
app.Main()
}
23 changes: 23 additions & 0 deletions agent/systemauth/pinentry/gioui-pinentry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//go:build windows || darwin

package pinentry

import (
"github.com/quexten/goldwarden/agent/systemauth/pinentry/giouipinentry"
)

func GetPassword(title string, description string) (string, error) {
var resultChan = make(chan string)
giouipinentry.GetPin(title, description, func(pin string) {
resultChan <- pin
})
return <-resultChan, nil
}

func GetApproval(title string, description string) (bool, error) {
var resultChan = make(chan bool)
giouipinentry.GetApproval(title, description, func(approved bool) {
resultChan <- approved
})
return <-resultChan, nil
}
310 changes: 310 additions & 0 deletions agent/systemauth/pinentry/giouipinentry/giopinentry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
// go:build windows || darwin
package giouipinentry

import (
"fmt"
"image"
"image/color"
"log"

"gioui.org/app"
"gioui.org/font/gofont"
"gioui.org/io/key"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
"gioui.org/widget/material"
)

func GetPin(title string, description string, onPinEntered func(string)) {
go func() {
w := app.NewWindow()
w.Option(app.Size(unit.Dp(500), unit.Dp(300)))
w.Option(app.Decorated(false))
w.Perform(system.ActionCenter)
w.Perform(system.ActionRaise)
if err := runPinentry(w, title, description, onPinEntered); err != nil {
log.Fatal(err)
}
}()
}

func GetApproval(title string, description string, onApproval func(bool)) {
go func() {
w := app.NewWindow()
w.Option(app.Size(unit.Dp(500), unit.Dp(300)))
w.Option(app.Decorated(false))
w.Perform(system.ActionCenter)
w.Perform(system.ActionRaise)
if err := runApproval(w, title, description, onApproval); err != nil {
log.Fatal(err)
}
}()
}

var (
// #000000
unselected = color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xFF}
// #FFFFFF
unselectedText = color.NRGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF}
// #000000
background = color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xFF}
// #651FFF
selected = color.NRGBA{R: 0x65, G: 0x1F, B: 0xFF, A: 0xFF}
// #FFFFFF
selectedText = color.NRGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF}
// #4caf50
buttonOk = color.NRGBA{R: 0x4C, G: 0xAF, B: 0x50, A: 0xFF}
// #F44336
buttonCancel = color.NRGBA{R: 0xF4, G: 0x43, B: 0x36, A: 0xFF}
)

var th = material.NewTheme(gofont.Collection())

func runPinentry(w *app.Window, title string, description string, onPinEntered func(string)) error {
var lineEditor = &widget.Editor{
SingleLine: true,
Submit: true,
}
lineEditor.Focus()
var ops op.Ops

var btnOk widget.Clickable
var btnCancel widget.Clickable

for {
e := <-w.Events()
switch e := e.(type) {
case system.DestroyEvent:
return e.Err
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)

key.InputOp{
Keys: key.Set(key.NameReturn + "|" + key.NameEscape + "|" + key.NameDownArrow),
Tag: 0,
}.Add(gtx.Ops)
t := lineEditor.Events()
for _, ev := range t {
switch ev.(type) {
case widget.SubmitEvent:
onPinEntered(lineEditor.Text())
w.Perform(system.ActionClose)
}
}
if btnOk.Clicked() {
onPinEntered(lineEditor.Text())
w.Perform(system.ActionClose)
}
if btnCancel.Clicked() {
onPinEntered("")
w.Perform(system.ActionClose)
}

test := gtx.Events(0)
for _, ev := range test {
switch ev := ev.(type) {
case key.Event:
switch ev.Name {
case key.NameReturn:
fmt.Println("uncaught submit")
return nil
case key.NameEscape:
onPinEntered("")
w.Perform(system.ActionClose)
}
}
}

Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
//title
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
title := material.H6(th, title)
title.Color = unselectedText
return title.Layout(gtx)
})
})
}),
// description
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
body := material.Body1(th, description)
body.Color = unselectedText
return body.Layout(gtx)
})
})
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
searchBox := material.Editor(th, lineEditor, "Pin")
searchBox.Color = selectedText
border := widget.Border{Color: selectedText, CornerRadius: unit.Dp(8), Width: unit.Dp(2)}
return border.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(8)).Layout(gtx, searchBox.Layout)
})
})
})
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Axis: layout.Vertical, Spacing: layout.SpaceStart}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
acceptBtn := material.Button(th, &btnOk, "Accept")
acceptBtn.Background = buttonOk
return acceptBtn.Layout(gtx)
}),
layout.Rigid(
layout.Spacer{Height: unit.Dp(10)}.Layout,
),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
cancel := material.Button(th, &btnCancel, "Cancel")
cancel.Background = buttonCancel
return cancel.Layout(gtx)
}),
)
})
}),
)
})

e.Frame(gtx.Ops)
}
}

return nil
}

func runApproval(w *app.Window, title string, description string, onApproval func(bool)) error {
var ops op.Ops

var btnOk widget.Clickable
var btnCancel widget.Clickable

for {
e := <-w.Events()
switch e := e.(type) {
case system.DestroyEvent:
return e.Err
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)

key.InputOp{
Keys: key.Set(key.NameReturn + "|" + key.NameEscape + "|" + key.NameDownArrow),
Tag: 0,
}.Add(gtx.Ops)

if btnOk.Clicked() {
onApproval(true)
w.Perform(system.ActionClose)
}
if btnCancel.Clicked() {
onApproval(false)
w.Perform(system.ActionClose)
}

test := gtx.Events(0)
for _, ev := range test {
switch ev := ev.(type) {
case key.Event:
switch ev.Name {
case key.NameReturn:
fmt.Println("uncaught submit")
return nil
case key.NameEscape:
onApproval(false)
w.Perform(system.ActionClose)
}
}
}

Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
//title
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
title := material.H6(th, title)
title.Color = unselectedText
return title.Layout(gtx)
})
})
}),
// description
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
body := material.Body1(th, description)
body.Color = unselectedText
return body.Layout(gtx)
})
})
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Axis: layout.Vertical, Spacing: layout.SpaceStart}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
acceptBtn := material.Button(th, &btnOk, "Accept")
acceptBtn.Background = buttonOk
return acceptBtn.Layout(gtx)
}),
layout.Rigid(
layout.Spacer{Height: unit.Dp(10)}.Layout,
),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
cancel := material.Button(th, &btnCancel, "Cancel")
cancel.Background = buttonCancel
return cancel.Layout(gtx)
}),
)
})
}),
)
})

e.Frame(gtx.Ops)
}
}

return nil
}

type Background struct {
Color color.NRGBA
CornerRadius unit.Dp
}

func (b Background) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
m := op.Record(gtx.Ops)
dims := w(gtx)
size := dims.Size
call := m.Stop()
if r := gtx.Dp(b.CornerRadius); r > 0 {
defer clip.RRect{
Rect: image.Rect(0, 0, size.X, size.Y),
NE: r, NW: r, SE: r, SW: r,
}.Push(gtx.Ops).Pop()
}
fill{b.Color}.Layout(gtx, size)
call.Add(gtx.Ops)
return dims
}

type fill struct {
col color.NRGBA
}

func (f fill) Layout(gtx layout.Context, sz image.Point) layout.Dimensions {
defer clip.Rect(image.Rectangle{Max: sz}).Push(gtx.Ops).Pop()
paint.ColorOp{Color: f.col}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops)
return layout.Dimensions{Size: sz}
}
2 changes: 1 addition & 1 deletion agent/systemauth/pinentry/go-pinentry.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux || freebsd
//go:build freebsd || linux

package pinentry

Expand Down
2 changes: 1 addition & 1 deletion agent/systemauth/pinentry/unimplemented.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !linux && !freebsd
//go:build !linux && !freebsd && !darwin && !windows

package pinentry

Expand Down
18 changes: 11 additions & 7 deletions agent/unixsocketagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,17 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
}
log.Info("Agent listening on %s...", path)

for {
fd, err := l.Accept()
if err != nil {
println("accept error", err.Error())
return err
go func() {
for {
fd, err := l.Accept()
if err != nil {
println("accept error", err.Error())
}

go serveAgentSession(fd, ctx, vault, &cfg)
}
}()

go serveAgentSession(fd, ctx, vault, &cfg)
}
mainloop()
return nil
}

0 comments on commit d09cc37

Please sign in to comment.