diff options
| author | raven <citrons@mondecitronne.com> | 2025-10-22 16:28:22 -0500 |
|---|---|---|
| committer | raven <citrons@mondecitronne.com> | 2026-02-09 13:13:55 -0600 |
| commit | ff0f6ba724748dbe340187fdd831a4d4f7f0ae5e (patch) | |
| tree | ab27a0c9f40d803c2bf130ef7c82cdf6540e9018 /client | |
| parent | 4b54a1d11fd0fa355b244637612a3fd0af18c60c (diff) | |
passwords
Diffstat (limited to 'client')
| -rw-r--r-- | client/application.go | 100 | ||||
| -rw-r--r-- | client/cmd_window.go | 8 | ||||
| -rw-r--r-- | client/command.go | 9 | ||||
| -rw-r--r-- | client/login_prompt.go | 96 | ||||
| -rw-r--r-- | client/ui.go | 8 |
5 files changed, 192 insertions, 29 deletions
diff --git a/client/application.go b/client/application.go index d396f24..640abd6 100644 --- a/client/application.go +++ b/client/application.go @@ -145,37 +145,109 @@ func (a *application) GetInfo(id string, callback func(*proto.Object)) { }) } -func (a *application) auth(name string, authCallback func(success bool)) { - callback := func(response proto.Command) { +func (a *application) authAnon(as string, callback func(ok bool, uid string)) { + cb := func(response proto.Command) { + switch response.Kind { + case "you-are": + if len(response.Args) == 0 { + callback(false, "") + break + } + me := response.Args[0] + a.onAuth(me.Id) + if callback != nil { + callback(true, me.Id) + } + case "fail": + var uid string + if len(response.Args) != 0 { + if response.Args[0].Kind == "name-taken" && + response.Args[0].Fields["anonymous"] == "no" { + uid = response.Args[0].Fields["id"] + } else { + a.cmdWindow.err(proto.Strfail(response.Args[0])) + } + } + if callback != nil { + callback(false, uid) + } + } + } + a.Request(proto.NewCmd("auth", "", proto.Object { + "anonymous", "", map[string]string {"": as}, + }), cb) +} + +func (a *application) authPassword( + uid string, password string, callback func(ok bool)) { + cb := func(response proto.Command) { switch response.Kind { case "you-are": if len(response.Args) == 0 { - authCallback(false) + callback(false) break } me := response.Args[0] - a.authenticated = true - a.uid = me.Id - a.cache.Watch(a.uid) - a.onAuth() - if authCallback != nil { - authCallback(true) + a.onAuth(me.Id) + if callback != nil { + callback(true) } case "fail": if len(response.Args) != 0 { a.cmdWindow.err(proto.Strfail(response.Args[0])) } - if authCallback != nil { - authCallback(false) + if callback != nil { + callback(false) } } } a.Request(proto.NewCmd("auth", "", proto.Object { - "anonymous", "", map[string]string {"": name}, - }), callback) + "password", "", map[string]string {"": password, "id": uid}, + }), cb) +} + +func (a *application) setPassword(password string, callback func(ok bool)) { + cb := func(response proto.Command) { + switch response.Kind { + case "ok": + if callback != nil { + callback(true) + } + case "fail": + if len(response.Args) != 0 && + response.Args[0].Kind == "password-required" { + lp := newLoginPrompt("") + lp.uid = a.uid + lp.customPrompt = "current password" + lp.callback = func(ok bool) { + if ok { + a.setPassword(password, callback) + } else { + if callback != nil { + callback(false) + } + } + } + a.pushPrompt(lp) + } else { + if len(response.Args) != 0 { + a.cmdWindow.err(proto.Strfail(response.Args[0])) + } + if callback != nil { + callback(false) + } + } + } + } + a.Request(proto.NewCmd("auth-update", a.uid, proto.Object { + "password", "", map[string]string {"": password}, + }), cb) } -func (a *application) onAuth() { +func (a *application) onAuth(uid string) { + a.authenticated = true + a.uid = uid + a.cache.Watch(uid) a.Request(proto.NewCmd("channels", ""), func(response proto.Command) { if response.Kind == "channels" { previousChannels := make(channelList, len(a.channelList)) diff --git a/client/cmd_window.go b/client/cmd_window.go index dccbaba..f849560 100644 --- a/client/cmd_window.go +++ b/client/cmd_window.go @@ -26,6 +26,7 @@ const ( logInfo = iota logErr logCmd + logWarn ) func (m logMsg) Id() string { @@ -39,6 +40,8 @@ func (m logMsg) Show(odd bool) { style = &tui.Style {Bg: colorErr[odd], Fg: tui.White} case logCmd: style = &tui.Style {Bg: colorCmd[odd], Fg: tui.White} + case logWarn: + style = &tui.Style {Bg: colorDefault[odd], Fg: 229} } tui.Push("", tui.Box { @@ -138,6 +141,11 @@ func (w *cmdWindow) info(f string, a ...any) { w.Buf.Add(logMsg {lastIndex, fmt.Sprintf(f, a...), logInfo}) } +func (w *cmdWindow) warn(f string, a ...any) { + lastIndex++ + w.Buf.Add(logMsg {lastIndex, fmt.Sprintf(f, a...), logWarn}) +} + func (w *cmdWindow) err(f string, a ...any) { lastIndex++ w.Buf.Add(logMsg {lastIndex, fmt.Sprintf(f, a...), logErr}) diff --git a/client/command.go b/client/command.go index 5ceb45b..64f8cd8 100644 --- a/client/command.go +++ b/client/command.go @@ -179,9 +179,14 @@ func (a *application) doCommand(command string, args []string, text string) { }) return case "create": - if a.authenticated { - a.createChannel(text) + a.createChannel(text) + return + case "password": + if text == "" { + a.cmdWindow.err("password cannot be empty") + return } + a.pushPrompt(&passwordChangePrompt {password: text}) return case "debug": a.goTo(debugWindowLocation{}) diff --git a/client/login_prompt.go b/client/login_prompt.go index c19d987..ec5b36b 100644 --- a/client/login_prompt.go +++ b/client/login_prompt.go @@ -7,7 +7,11 @@ import ( type loginPrompt struct { input tui.TextInput + uid string username string + customPrompt string + waiting bool + callback func(ok bool) } func newLoginPrompt(completeName string) *loginPrompt { @@ -21,28 +25,96 @@ func newLoginPrompt(completeName string) *loginPrompt { } func (p *loginPrompt) Input() *tui.TextInput { - return &p.input + if !p.waiting { + return &p.input + } else { + return &tui.TextInput {} + } } func (p *loginPrompt) Send(text string) { - if p.username != "" { - return - } - p.username = text - globalApp.auth(text, func(success bool) { - if success { + if p.uid == "" { + p.username = text + globalApp.authAnon(text, func(success bool, uid string) { + if success { + globalApp.removePrompt(p) + globalApp.cmdWindow.warn( + "you have created a temporary account. " + + "set a password to keep it.", + ) + } else if uid == "" { + p.username = "" + } else { + p.uid = uid + p.input.SetText("") + p.input.Private = true + } + }) + } else { + p.input.SetText("") + p.waiting = true + globalApp.authPassword(p.uid, text, func(success bool) { + p.waiting = false + if !globalApp.authenticated { + p.uid = "" + p.input.SetText(p.username) + p.input.Private = false + return + } globalApp.removePrompt(p) + if p.callback != nil { + p.callback(success) + } + }) + } +} + +func (p *loginPrompt) ShowStatusLine() { + tui.Text("[", nil) + tui.Text("login", &tui.Style { + Bg: tui.White, Fg: tui.Blue, Bold: true, + }) + tui.Text("] ", nil) + if p.customPrompt == "" { + if p.uid == "" { + tui.Text("username", nil) } else { - p.username = "" + tui.Text("password", nil) } - }) + } else { + tui.Text(p.customPrompt, nil) + } + tui.Text(":", nil) } -func (p *loginPrompt) ShowStatusLine() { +type passwordChangePrompt struct { + input tui.TextInput + password string +} + +func (p *passwordChangePrompt) Input() *tui.TextInput { + p.input.Private = true + return &p.input +} + +func (p *passwordChangePrompt) Send(text string) { + if text != p.password { + globalApp.cmdWindow.err("passwords do not match") + } else { + globalApp.setPassword(text, func(ok bool) { + if ok { + globalApp.cmdWindow.info("password changed") + } + }) + } + globalApp.removePrompt(p) +} + +func (p *passwordChangePrompt) ShowStatusLine() { tui.Text("[", nil) tui.Text("login", &tui.Style { Bg: tui.White, Fg: tui.Blue, Bold: true, }) - tui.Text("]", nil) - tui.Text(" username:", nil) + tui.Text("] ", nil) + tui.Text("confirm new password:", nil) } diff --git a/client/ui.go b/client/ui.go index e5917fa..6ea9fa5 100644 --- a/client/ui.go +++ b/client/ui.go @@ -190,7 +190,13 @@ func (a *application) showWindow() { Width: tui.Fill, Height: tui.Children, Dir: tui.Right, }) a.showNickBox() - prompt.Input().Show("input") + input := prompt.Input() + priv := input.Private + if strings.HasPrefix(input.Text(), "/") { + input.Private = strings.HasPrefix(input.Text(), "/password ") + } + input.Show("input") + input.Private = priv tui.Pop() tui.Pop() |
