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 }