github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/engine.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package engine 5 6 import ( 7 "fmt" 8 "runtime/debug" 9 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 type Prereqs = libkb.EnginePrereqs 15 type Engine2 = libkb.Engine2 16 17 type UIDelegateWanter interface { 18 WantDelegate(libkb.UIKind) bool 19 } 20 21 func requiresUI(c libkb.UIConsumer, kind libkb.UIKind) bool { 22 for _, ui := range c.RequiredUIs() { 23 if ui == kind { 24 return true 25 } 26 } 27 for _, sub := range c.SubConsumers() { 28 if requiresUI(sub, kind) { 29 return true 30 } 31 } 32 return false 33 } 34 35 // isLoggedInWithUIDAndError conveys if the user is in a logged-in state or not. 36 // If this function returns `true`, it's because the user is logged in, 37 // is on a provisioned device, and has an unlocked device key, If this 38 // function returns `false`, it's because either no one has ever logged onto 39 // this device, or someone has, and then clicked `logout`. If the return 40 // value is `false`, and `err` is `nil`, then the service is in one of 41 // those expected "logged out" states. If the return value is `false` 42 // and `err` is non-`nil`, then something went wrong, and the app is in some 43 // sort of unexpected state. If `ret` is `true`, then `uid` will convey 44 // which user is logged in. 45 // 46 // Under the hood, IsLoggedIn is going through the BootstrapActiveDevice 47 // flow and therefore will try its best to unlocked locked keys if it can 48 // without user interaction. 49 // 50 // If the user is intentionally not logged into any user, don't try to 51 // bootstrap from the secret store and just check if there is an active device. 52 func isLoggedInWithUIDAndError(m libkb.MetaContext) (ret bool, uid keybase1.UID, err error) { 53 if m.G().Env.GetStayLoggedOut() { 54 return m.ActiveDevice().Valid(), m.G().Env.GetUID(), nil 55 } 56 ret, uid, err = libkb.BootstrapActiveDeviceWithMetaContext(m) 57 return ret, uid, err 58 } 59 60 func isLoggedIn(m libkb.MetaContext) (ret bool, uid keybase1.UID) { 61 if m.G().Env.GetStayLoggedOut() { 62 return m.ActiveDevice().Valid(), m.G().Env.GetUID() 63 } 64 ret, uid, _ = libkb.BootstrapActiveDeviceWithMetaContext(m) 65 return ret, uid 66 } 67 68 func isLoggedInAs(m libkb.MetaContext, uid keybase1.UID) (ret bool) { 69 if m.G().Env.GetStayLoggedOut() { 70 return m.ActiveDevice().Valid() && uid == m.G().Env.GetUID() 71 } 72 ret, err := libkb.BootstrapActiveDeviceWithMetaContextAndAssertUID(m, uid) 73 if err != nil { 74 m.Debug("isLoggedAs error: %s", err) 75 } 76 return ret 77 } 78 79 func isLoggedInWithError(m libkb.MetaContext) (ret bool, err error) { 80 ret, _, err = isLoggedInWithUIDAndError(m) 81 return ret, err 82 } 83 84 func runPrereqs(m libkb.MetaContext, e Engine2) error { 85 prq := e.Prereqs() 86 87 if prq.TemporarySession { 88 if !m.HasAnySession() { 89 return libkb.NewLoginRequiredError("need either a temporary session or a device") 90 } 91 } 92 93 if prq.Device { 94 ok, err := isLoggedInWithError(m) 95 if err != nil { 96 return err 97 } 98 if !ok { 99 return libkb.DeviceRequiredError{} 100 } 101 } 102 103 return nil 104 105 } 106 107 func RunEngine2(m libkb.MetaContext, e Engine2) (err error) { 108 m = m.WithLogTag("ENG") 109 defer m.Trace(fmt.Sprintf("RunEngine(%s)", e.Name()), &err)() 110 111 if m, err = delegateUIs(m, e); err != nil { 112 return err 113 } 114 if err = check(m, e); err != nil { 115 return err 116 } 117 if err = runPrereqs(m, e); err != nil { 118 return err 119 } 120 121 err = e.Run(m) 122 return err 123 } 124 125 func getIdentifyUI3or1(m libkb.MetaContext) (libkb.IdentifyUI, error) { 126 uir := m.G().UIRouter 127 ret, err := uir.GetIdentify3UIAdapter(m) 128 if ret != nil && err == nil { 129 return ret, err 130 } 131 return uir.GetIdentifyUI() 132 } 133 134 func delegateUIs(m libkb.MetaContext, e Engine2) (libkb.MetaContext, error) { 135 if m.G().UIRouter == nil { 136 return m, nil 137 } 138 139 // currently, only doing this for SecretUI, but in future, 140 // perhaps should iterate over all registered UIs in UIRouter. 141 if requiresUI(e, libkb.SecretUIKind) { 142 sessionID := m.UIs().SessionID 143 if ui, err := m.G().UIRouter.GetSecretUI(sessionID); err != nil { 144 return m, err 145 } else if ui != nil { 146 m.Debug("using delegated secret UI for engine %q (session id = %d)", e.Name(), sessionID) 147 m = m.WithSecretUI(ui) 148 } 149 } 150 151 if wantsDelegateUI(e, libkb.IdentifyUIKind) { 152 m.Debug("IdentifyUI wanted for engine %q", e.Name()) 153 ui, err := getIdentifyUI3or1(m) 154 if err != nil { 155 return m, err 156 } 157 if ui != nil { 158 m.Debug("using delegated identify UI for engine %q", e.Name()) 159 m = m.WithDelegatedIdentifyUI(ui) 160 } 161 } 162 return m, nil 163 } 164 165 func wantsDelegateUI(e Engine2, kind libkb.UIKind) bool { 166 if !requiresUI(e, kind) { 167 return false 168 } 169 if i, ok := e.(UIDelegateWanter); ok { 170 return i.WantDelegate(kind) 171 } 172 return false 173 } 174 175 func check(m libkb.MetaContext, c libkb.UIConsumer) error { 176 if err := checkUI(m, c); err != nil { 177 return err 178 } 179 180 for _, sub := range c.SubConsumers() { 181 if err := check(m, sub); err != nil { 182 if _, ok := err.(CheckError); ok { 183 return err 184 } 185 return CheckError{fmt.Sprintf("%s: %s", sub.Name(), err)} 186 } 187 } 188 189 return nil 190 } 191 192 func checkUI(m libkb.MetaContext, c libkb.UIConsumer) error { 193 for _, ui := range c.RequiredUIs() { 194 if !m.UIs().HasUI(ui) { 195 return CheckError{fmt.Sprintf("%s: requires ui %q\n\n%s", c.Name(), ui, string(debug.Stack()))} 196 } 197 } 198 return nil 199 }