github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/service/account.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 service
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"time"
    10  
    11  	"github.com/keybase/client/go/engine"
    12  	"github.com/keybase/client/go/libkb"
    13  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    14  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    15  	"golang.org/x/net/context"
    16  )
    17  
    18  type AccountHandler struct {
    19  	*BaseHandler
    20  	libkb.Contextified
    21  }
    22  
    23  func NewAccountHandler(xp rpc.Transporter, g *libkb.GlobalContext) *AccountHandler {
    24  	return &AccountHandler{
    25  		BaseHandler:  NewBaseHandler(g, xp),
    26  		Contextified: libkb.NewContextified(g),
    27  	}
    28  }
    29  
    30  func (h *AccountHandler) getDelegateSecretUI(sessionID int) (ret libkb.SecretUI, err error) {
    31  	if h.G().UIRouter != nil {
    32  		return h.G().UIRouter.GetSecretUI(sessionID)
    33  	}
    34  	return nil, nil
    35  }
    36  
    37  func (h *AccountHandler) PassphraseChange(ctx context.Context, arg keybase1.PassphraseChangeArg) error {
    38  	eng := engine.NewPassphraseChange(h.G(), &arg)
    39  	uis := libkb.UIs{
    40  		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
    41  		SessionID: arg.SessionID,
    42  	}
    43  	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
    44  	return engine.RunEngine2(m, eng)
    45  }
    46  
    47  func (h *AccountHandler) PassphrasePrompt(_ context.Context, arg keybase1.PassphrasePromptArg) (keybase1.GetPassphraseRes, error) {
    48  	ui := h.getSecretUI(arg.SessionID, h.G())
    49  	if delegateUI, err := h.getDelegateSecretUI(arg.SessionID); err != nil {
    50  		return keybase1.GetPassphraseRes{}, err
    51  	} else if delegateUI != nil {
    52  		ui = delegateUI
    53  		h.G().Log.Debug("using delegate secret UI")
    54  	}
    55  
    56  	return ui.GetPassphrase(arg.GuiArg, nil)
    57  }
    58  
    59  func (h *AccountHandler) EmailChange(nctx context.Context, arg keybase1.EmailChangeArg) error {
    60  	uis := libkb.UIs{
    61  		SessionID: arg.SessionID,
    62  		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
    63  	}
    64  	m := libkb.NewMetaContext(nctx, h.G()).WithUIs(uis)
    65  	eng := engine.NewEmailChange(h.G(), &arg)
    66  	return engine.RunEngine2(m, eng)
    67  }
    68  
    69  func (h *AccountHandler) HasServerKeys(ctx context.Context, sessionID int) (res keybase1.HasServerKeysRes, err error) {
    70  	arg := keybase1.HasServerKeysArg{SessionID: sessionID}
    71  	eng := engine.NewHasServerKeys(h.G())
    72  	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(libkb.UIs{SessionID: arg.SessionID})
    73  	err = engine.RunEngine2(m, eng)
    74  	if err != nil {
    75  		return res, err
    76  	}
    77  	return eng.GetResult(), nil
    78  }
    79  
    80  func (h *AccountHandler) ResetAccount(ctx context.Context, arg keybase1.ResetAccountArg) (err error) {
    81  
    82  	if h.G().Env.GetRunMode() != libkb.DevelRunMode {
    83  		return errors.New("ResetAccount only supported in devel run mode")
    84  	}
    85  
    86  	m := libkb.NewMetaContext(ctx, h.G())
    87  	defer m.Trace("AccountHandler#ResetAccount", &err)()
    88  
    89  	username := h.G().GetEnv().GetUsername()
    90  	m.Debug("resetting account for %s", username.String())
    91  
    92  	passphrase := arg.Passphrase
    93  	if passphrase == "" {
    94  		pparg := libkb.DefaultPassphrasePromptArg(m, username.String())
    95  		secretUI := h.getSecretUI(arg.SessionID, h.G())
    96  		res, err := secretUI.GetPassphrase(pparg, nil)
    97  		if err != nil {
    98  			return err
    99  		}
   100  		passphrase = res.Passphrase
   101  	}
   102  
   103  	err = libkb.ResetAccount(m, username, passphrase)
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	m.Debug("reset account succeeded, logging out.")
   109  
   110  	return m.LogoutKillSecrets()
   111  }
   112  
   113  type GetLockdownResponse struct {
   114  	libkb.AppStatusEmbed
   115  	Enabled bool                       `json:"enabled"`
   116  	History []keybase1.LockdownHistory `json:"history"`
   117  }
   118  
   119  func (h *AccountHandler) GetLockdownMode(ctx context.Context, sessionID int) (ret keybase1.GetLockdownResponse, err error) {
   120  	mctx := libkb.NewMetaContext(ctx, h.G())
   121  	defer mctx.Trace("GetLockdownMode", &err)()
   122  	apiArg := libkb.APIArg{
   123  		Endpoint:    "account/lockdown",
   124  		SessionType: libkb.APISessionTypeREQUIRED,
   125  	}
   126  	var response GetLockdownResponse
   127  	err = mctx.G().API.GetDecode(mctx, apiArg, &response)
   128  	if err != nil {
   129  		return ret, err
   130  	}
   131  
   132  	upak, _, err := mctx.G().GetUPAKLoader().Load(
   133  		libkb.NewLoadUserArgWithMetaContext(mctx).WithPublicKeyOptional().WithUID(mctx.G().ActiveDevice.UID()))
   134  	if err != nil {
   135  		return ret, err
   136  	}
   137  
   138  	// Fill device names from ActiveDevices list.
   139  	for i, v := range response.History {
   140  		dev := upak.FindDevice(v.DeviceID)
   141  		if dev == nil {
   142  			mctx.Debug("GetLockdownMode: Could not find device in UserPlusAllKeys: %s", v.DeviceID)
   143  			continue
   144  		}
   145  
   146  		v.DeviceName = dev.DeviceDescription
   147  		response.History[i] = v
   148  	}
   149  
   150  	ret = keybase1.GetLockdownResponse{
   151  		Status:  response.Enabled,
   152  		History: response.History,
   153  	}
   154  	h.G().Log.CDebugf(ctx, "GetLockdownMode -> %v", ret.Status)
   155  	return ret, nil
   156  }
   157  
   158  func (h *AccountHandler) SetLockdownMode(ctx context.Context, arg keybase1.SetLockdownModeArg) (err error) {
   159  	mctx := libkb.NewMetaContext(ctx, h.G())
   160  	defer mctx.Trace(fmt.Sprintf("SetLockdownMode(%v)", arg.Enabled), &err)()
   161  	apiArg := libkb.APIArg{
   162  		Endpoint:    "account/lockdown",
   163  		SessionType: libkb.APISessionTypeREQUIRED,
   164  		Args: libkb.HTTPArgs{
   165  			"enabled": libkb.B{Val: arg.Enabled},
   166  		},
   167  	}
   168  	_, err = mctx.G().API.Post(mctx, apiArg)
   169  	return err
   170  }
   171  
   172  func (h *AccountHandler) PassphraseCheck(ctx context.Context, arg keybase1.PassphraseCheckArg) (ret bool, err error) {
   173  	mctx := libkb.NewMetaContext(ctx, h.G())
   174  	defer mctx.Trace("PassphraseCheck", &err)()
   175  	eng := engine.NewPassphraseCheck(mctx.G(), &arg)
   176  	uis := libkb.UIs{
   177  		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
   178  		SessionID: arg.SessionID,
   179  	}
   180  	err = eng.Run(mctx.WithUIs(uis))
   181  	return eng.GetResult(), err
   182  }
   183  
   184  func (h *AccountHandler) RecoverUsernameWithEmail(ctx context.Context, arg keybase1.RecoverUsernameWithEmailArg) (err error) {
   185  	mctx := libkb.NewMetaContext(ctx, h.G())
   186  	defer mctx.Trace(fmt.Sprintf("RecoverUsernameWithEmail(%q)", arg.Email), &err)()
   187  	apiArg := libkb.APIArg{
   188  		Endpoint:    "account/recover_username",
   189  		SessionType: libkb.APISessionTypeNONE,
   190  		Args: libkb.HTTPArgs{
   191  			"email": libkb.S{Val: arg.Email},
   192  		},
   193  	}
   194  	_, err = mctx.G().API.Post(mctx, apiArg)
   195  	return err
   196  }
   197  
   198  func (h *AccountHandler) RecoverUsernameWithPhone(ctx context.Context, arg keybase1.RecoverUsernameWithPhoneArg) (err error) {
   199  	mctx := libkb.NewMetaContext(ctx, h.G())
   200  	defer mctx.Trace(fmt.Sprintf("RecoverUsernameWithPhone(%q)", arg.Phone), &err)()
   201  	if err = libkb.IsPossiblePhoneNumber(arg.Phone); err != nil {
   202  		return err
   203  	}
   204  	apiArg := libkb.APIArg{
   205  		Endpoint:    "account/recover_username",
   206  		SessionType: libkb.APISessionTypeNONE,
   207  		Args: libkb.HTTPArgs{
   208  			"phone_number": libkb.S{Val: arg.Phone.String()},
   209  		},
   210  	}
   211  	_, err = mctx.G().API.Post(mctx, apiArg)
   212  	return err
   213  }
   214  
   215  // EnterPipeline allows a user to enter the reset pipeline. The user must
   216  // verify ownership of the account via an email confirmation or their password.
   217  // Resets are not allowed on a provisioned device.
   218  func (h *AccountHandler) EnterResetPipeline(ctx context.Context, arg keybase1.EnterResetPipelineArg) (err error) {
   219  	mctx := libkb.NewMetaContext(ctx, h.G())
   220  	defer mctx.Trace("EnterResetPipline", &err)()
   221  	uis := libkb.UIs{
   222  		LoginUI:   h.getLoginUI(arg.SessionID),
   223  		SecretUI:  h.getSecretUI(arg.SessionID, h.G()),
   224  		LogUI:     h.getLogUI(arg.SessionID),
   225  		SessionID: arg.SessionID,
   226  	}
   227  	eng := engine.NewAccountReset(h.G(), arg.UsernameOrEmail)
   228  	// In noninteractive mode, use this to set skipPassword
   229  	if arg.Passphrase != "" || !arg.Interactive {
   230  		eng.SetPassphrase(arg.Passphrase)
   231  	}
   232  	m := libkb.NewMetaContext(ctx, h.G()).WithUIs(uis)
   233  	return engine.RunEngine2(m, eng)
   234  }
   235  
   236  // CancelReset allows a user to cancel the reset process via an authenticated API call.
   237  func (h *AccountHandler) CancelReset(ctx context.Context, sessionID int) (err error) {
   238  	mctx := libkb.NewMetaContext(ctx, h.G())
   239  	defer mctx.Trace("CancelReset", &err)()
   240  	err = libkb.CancelResetPipeline(mctx)
   241  	if err != nil {
   242  		mctx.Debug("CancelResetPipeline failed with: %s", err)
   243  		mctx.Debug("Checking if we are not revoked")
   244  		err2 := mctx.LogoutAndDeprovisionIfRevoked()
   245  		if err2 != nil {
   246  			mctx.Error("LogoutAndDeprovisionIfRevoked failed in CancelReset check: %s", err2)
   247  			return libkb.CombineErrors(err, err2)
   248  		}
   249  		if mctx.CurrentUID().IsNil() {
   250  			// We got logged out.
   251  			return UserWasLoggedOutError{}
   252  		}
   253  	}
   254  	return err
   255  }
   256  
   257  // TimeTravelReset allows a user to move forward in the reset process via an API call [devel-only].
   258  func (h *AccountHandler) TimeTravelReset(ctx context.Context, arg keybase1.TimeTravelResetArg) error {
   259  	mctx := libkb.NewMetaContext(ctx, h.G())
   260  	if arg.Username == "" {
   261  		current, _, err := mctx.G().GetAllUserNames()
   262  		if err != nil {
   263  			return err
   264  		}
   265  		arg.Username = current.String()
   266  	}
   267  
   268  	_, err := mctx.G().API.Post(mctx, libkb.APIArg{
   269  		Endpoint:    "autoreset/timetravel",
   270  		SessionType: libkb.APISessionTypeNONE,
   271  		Args: libkb.HTTPArgs{
   272  			"username":     libkb.S{Val: arg.Username},
   273  			"duration_sec": libkb.I{Val: int(arg.Duration)},
   274  		},
   275  	})
   276  
   277  	return err
   278  }
   279  
   280  func (h *AccountHandler) GuessCurrentLocation(ctx context.Context, arg keybase1.GuessCurrentLocationArg) (string, error) {
   281  	mctx := libkb.NewMetaContext(ctx, h.G())
   282  	res, err := mctx.G().API.Get(mctx, libkb.APIArg{
   283  		Endpoint:       "account/location_suggest",
   284  		SessionType:    libkb.APISessionTypeNONE,
   285  		InitialTimeout: 2500 * time.Millisecond,
   286  		RetryCount:     2,
   287  	})
   288  	if err != nil {
   289  		mctx.Warning("Unable to retrieve the current location: %v", err)
   290  		return arg.DefaultCountry, nil
   291  	}
   292  	code, err := res.Body.AtKey("country_code").GetString()
   293  	if err != nil || code == "-" {
   294  		mctx.Warning("Unable to retrieve the current location: %v", err)
   295  		return arg.DefaultCountry, nil
   296  	}
   297  	mctx.Debug("Guessed this device's country to be %v", code)
   298  	return code, nil
   299  }
   300  
   301  func (h *AccountHandler) UserGetContactSettings(ctx context.Context) (res keybase1.ContactSettings, err error) {
   302  	mctx := libkb.NewMetaContext(ctx, h.G())
   303  	defer mctx.Trace("AccountHandler#UserGetContactSettings", &err)()
   304  	return libkb.GetContactSettings(mctx)
   305  }
   306  
   307  func (h *AccountHandler) UserSetContactSettings(ctx context.Context, arg keybase1.ContactSettings) (err error) {
   308  	mctx := libkb.NewMetaContext(ctx, h.G())
   309  	defer mctx.Trace("AccountHandler#UserSetContactSettings", &err)()
   310  	return libkb.SetContactSettings(mctx, arg)
   311  }