code.vegaprotocol.io/vega@v0.79.0/wallet/api/client_list_keys.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package api
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  
    23  	"code.vegaprotocol.io/vega/libs/jsonrpc"
    24  	"code.vegaprotocol.io/vega/wallet/wallet"
    25  )
    26  
    27  const PermissionsSuccessfullyUpdated = "The permissions have been successfully updated."
    28  
    29  type ClientListKeysResult struct {
    30  	Keys []ClientNamedPublicKey `json:"keys"`
    31  }
    32  
    33  type ClientNamedPublicKey struct {
    34  	Name      string `json:"name"`
    35  	PublicKey string `json:"publicKey"`
    36  }
    37  
    38  type ClientListKeys struct {
    39  	walletStore WalletStore
    40  	interactor  Interactor
    41  }
    42  
    43  // Handle returns the public keys the third-party application has access to.
    44  //
    45  // This requires a "read" access on "public_keys".
    46  func (h *ClientListKeys) Handle(ctx context.Context, connectedWallet ConnectedWallet) (jsonrpc.Result, *jsonrpc.ErrorDetails) {
    47  	traceID := jsonrpc.TraceIDFromContext(ctx)
    48  
    49  	if !connectedWallet.CanListKeys() {
    50  		if err := h.updatePublicKeysPermissions(ctx, traceID, &connectedWallet); err != nil {
    51  			return nil, err
    52  		}
    53  	}
    54  
    55  	allowedKeys := connectedWallet.AllowedKeys()
    56  
    57  	keys := make([]ClientNamedPublicKey, 0, len(allowedKeys))
    58  	for _, allowedKey := range allowedKeys {
    59  		keys = append(keys, ClientNamedPublicKey{
    60  			Name:      allowedKey.Name(),
    61  			PublicKey: allowedKey.PublicKey(),
    62  		})
    63  	}
    64  
    65  	return ClientListKeysResult{
    66  		Keys: keys,
    67  	}, nil
    68  }
    69  
    70  func (h *ClientListKeys) updatePublicKeysPermissions(ctx context.Context, traceID string, connectedWallet *ConnectedWallet) *jsonrpc.ErrorDetails {
    71  	if err := h.interactor.NotifyInteractionSessionBegan(ctx, traceID, PermissionRequestWorkflow, 2); err != nil {
    72  		return RequestNotPermittedError(err)
    73  	}
    74  	defer h.interactor.NotifyInteractionSessionEnded(ctx, traceID)
    75  
    76  	freshWallet, err := h.walletStore.GetWallet(ctx, connectedWallet.Name())
    77  	if err != nil {
    78  		if errors.Is(err, ErrWalletIsLocked) {
    79  			h.interactor.NotifyError(ctx, traceID, ApplicationErrorType, err)
    80  		} else {
    81  			h.interactor.NotifyError(ctx, traceID, InternalErrorType, fmt.Errorf("could not retrieve the wallet for the permissions update: %w", err))
    82  		}
    83  		return InternalError(ErrCouldNotListKeys)
    84  	}
    85  
    86  	perms := freshWallet.Permissions(connectedWallet.Hostname())
    87  
    88  	// At this point, we need a "read" access on public keys.
    89  	perms.PublicKeys.Access = wallet.ReadAccess
    90  	approved, err := h.interactor.RequestPermissionsReview(ctx, traceID, 1, connectedWallet.Hostname(), connectedWallet.Name(), perms.Summary())
    91  	if err != nil {
    92  		if errDetails := HandleRequestFlowError(ctx, traceID, h.interactor, err); errDetails != nil {
    93  			return errDetails
    94  		}
    95  		h.interactor.NotifyError(ctx, traceID, InternalErrorType, fmt.Errorf("requesting the permissions review failed: %w", err))
    96  		return InternalError(ErrCouldNotListKeys)
    97  	}
    98  	if !approved {
    99  		return UserRejectionError(ErrUserRejectedAccessToKeys)
   100  	}
   101  
   102  	if err := freshWallet.UpdatePermissions(connectedWallet.Hostname(), perms); err != nil {
   103  		h.interactor.NotifyError(ctx, traceID, InternalErrorType, fmt.Errorf("could not update the permissions on the wallet: %w", err))
   104  		return InternalError(ErrCouldNotListKeys)
   105  	}
   106  
   107  	if err := connectedWallet.RefreshFromWallet(freshWallet); err != nil {
   108  		h.interactor.NotifyError(ctx, traceID, InternalErrorType, fmt.Errorf("could not refresh the connection information after the permissions update: %w", err))
   109  		return InternalError(ErrCouldNotListKeys)
   110  	}
   111  
   112  	if err := h.walletStore.UpdateWallet(ctx, freshWallet); err != nil {
   113  		h.interactor.NotifyError(ctx, traceID, InternalErrorType, fmt.Errorf("could not save the permissions update on the wallet: %w", err))
   114  		return InternalError(ErrCouldNotListKeys)
   115  	}
   116  
   117  	h.interactor.NotifySuccessfulRequest(ctx, traceID, 2, PermissionsSuccessfullyUpdated)
   118  
   119  	return nil
   120  }
   121  
   122  func NewListKeys(walletStore WalletStore, interactor Interactor) *ClientListKeys {
   123  	return &ClientListKeys{
   124  		walletStore: walletStore,
   125  		interactor:  interactor,
   126  	}
   127  }