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  }