github.com/hyperledger/aries-framework-go@v0.3.2/pkg/wallet/session.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  Copyright Avast Software. All Rights Reserved.
     4  
     5  SPDX-License-Identifier: Apache-2.0
     6  */
     7  
     8  package wallet
     9  
    10  import (
    11  	"crypto/rand"
    12  	"encoding/hex"
    13  	"fmt"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/bluele/gcache"
    18  	"github.com/pkg/errors"
    19  
    20  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    21  )
    22  
    23  // ErrInvalidAuthToken when auth token provided to wallet is unable to unlock key manager.
    24  var ErrInvalidAuthToken = errors.New("invalid auth token")
    25  
    26  const (
    27  	// default cache expiry time.
    28  	defaultCacheExpiry = 10 * time.Minute
    29  	sessionTokenSize   = 32
    30  )
    31  
    32  // Session represent a session object created when user unlock wallet.
    33  type Session struct {
    34  	KeyManager    kms.KeyManager
    35  	sessionExpiry time.Duration
    36  	user          string
    37  }
    38  
    39  // sessionManagerInstance is key manager store singleton - access only via sessionManager()
    40  //
    41  //nolint:gochecknoglobals
    42  var (
    43  	sessionManagerInstance  *walletSessionManager
    44  	sessionManagerStoreOnce sync.Once
    45  )
    46  
    47  func sessionManager() *walletSessionManager {
    48  	sessionManagerStoreOnce.Do(func() {
    49  		sessionManagerInstance = &walletSessionManager{
    50  			gstore: gcache.New(0).Build(),
    51  		}
    52  	})
    53  
    54  	return sessionManagerInstance
    55  }
    56  
    57  type walletSessionManager struct {
    58  	gstore gcache.Cache
    59  	mu     sync.Mutex
    60  }
    61  
    62  func (s *walletSessionManager) createSession(userID string, keyManager kms.KeyManager,
    63  	sessionExpiry time.Duration) (string, error) {
    64  	if sessionExpiry == 0 {
    65  		sessionExpiry = defaultCacheExpiry
    66  	}
    67  
    68  	session := &Session{
    69  		KeyManager:    keyManager,
    70  		sessionExpiry: sessionExpiry,
    71  		user:          userID,
    72  	}
    73  
    74  	s.mu.Lock()
    75  	defer s.mu.Unlock()
    76  
    77  	for _, sess := range s.gstore.GetALL(true) {
    78  		if sess.(*Session).user == userID {
    79  			return "", ErrAlreadyUnlocked
    80  		}
    81  	}
    82  
    83  	for {
    84  		token, err := s.generateToken()
    85  		if err != nil {
    86  			return "", err
    87  		}
    88  
    89  		if !s.gstore.Has(token) {
    90  			err = s.gstore.SetWithExpire(token, session, sessionExpiry)
    91  
    92  			if err != nil {
    93  				return "", fmt.Errorf("set with expire failed: %w", err)
    94  			}
    95  
    96  			return token, nil
    97  		}
    98  	}
    99  }
   100  
   101  func (s *walletSessionManager) getSession(authToken string) (*Session, error) {
   102  	sess, err := s.gstore.Get(authToken)
   103  	if err != nil {
   104  		if errors.Is(err, gcache.KeyNotFoundError) {
   105  			return nil, ErrInvalidAuthToken
   106  		}
   107  
   108  		return nil, fmt.Errorf("failed to get session object: %w", err)
   109  	}
   110  
   111  	session, ok := sess.(*Session)
   112  	if !ok {
   113  		return nil, fmt.Errorf("failed to cast session object: expects Session, gets %T", sess)
   114  	}
   115  
   116  	err = s.gstore.SetWithExpire(authToken, session, session.sessionExpiry)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("set with expire failed: %w", err)
   119  	}
   120  
   121  	return session, nil
   122  }
   123  
   124  func wrapSessionError(err error) error {
   125  	if errors.Is(err, ErrInvalidAuthToken) {
   126  		return ErrWalletLocked
   127  	}
   128  
   129  	return fmt.Errorf("failed to get session: %w", err)
   130  }
   131  
   132  func (s *walletSessionManager) closeSession(userID string) bool {
   133  	s.mu.Lock()
   134  	defer s.mu.Unlock()
   135  
   136  	for token, sess := range s.gstore.GetALL(true) {
   137  		if sess.(*Session).user == userID {
   138  			return s.gstore.Remove(token)
   139  		}
   140  	}
   141  
   142  	return false
   143  }
   144  
   145  func (s *walletSessionManager) generateToken() (string, error) {
   146  	tokenBytes := make([]byte, sessionTokenSize)
   147  
   148  	_, err := rand.Read(tokenBytes)
   149  	if err != nil {
   150  		return "", fmt.Errorf("failed to create random sessoin token: %w", err)
   151  	}
   152  
   153  	return hex.EncodeToString(tokenBytes), nil
   154  }