-
Notifications
You must be signed in to change notification settings - Fork 19
/
sessions.go
179 lines (145 loc) · 4.97 KB
/
sessions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package api
import (
"errors"
"fmt"
vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
vgrand "code.vegaprotocol.io/vega/libs/rand"
"code.vegaprotocol.io/vega/wallet/wallet"
)
var ErrNoWalletConnected = errors.New("no wallet connected")
type Sessions struct {
// fingerprints holds the hash of the wallet and the hostname in use in the
// sessions as key and the token as value. It's used to retrieve the token
// if the client quit the third-party application without disconnecting the
// wallet first.
fingerprints map[string]string
// connectedWallets holds the wallet resources and information by token.
connectedWallets map[string]*ConnectedWallet
}
// ConnectWallet initiates a wallet connection and load associated resources in
// it. If a connection already exists, it's disconnected and a new token is
// generated.
func (s *Sessions) ConnectWallet(hostname string, w wallet.Wallet) (string, error) {
fingerprint := toFingerprint(hostname, w)
if token, ok := s.fingerprints[fingerprint]; ok {
// We already have a connection for that wallet and hostname, we destroy
// it.
s.DisconnectWallet(token)
}
connectedWallet, err := NewConnectedWallet(hostname, w)
if err != nil {
return "", fmt.Errorf("could not load the wallet: %w", err)
}
token := vgrand.RandomStr(64)
s.fingerprints[fingerprint] = token
s.connectedWallets[token] = connectedWallet
return token, nil
}
// DisconnectWallet unloads the connected wallet resources and revokes the token.
// It does not fail. Non-existing token does nothing.
func (s *Sessions) DisconnectWallet(token string) {
connectedWallet, ok := s.connectedWallets[token]
if !ok {
return
}
fingerprint := toFingerprint(connectedWallet.Hostname, connectedWallet.Wallet)
delete(s.fingerprints, fingerprint)
delete(s.connectedWallets, token)
}
// GetConnectedWallet retrieves the resources and information of the
// connected wallet, associated to the specified token.
func (s *Sessions) GetConnectedWallet(token string) (*ConnectedWallet, error) {
connectedWallet, ok := s.connectedWallets[token]
if !ok {
return nil, ErrNoWalletConnected
}
return connectedWallet, nil
}
func NewSessions() *Sessions {
return &Sessions{
fingerprints: map[string]string{},
connectedWallets: map[string]*ConnectedWallet{},
}
}
// ConnectedWallet contains the resources and the information of the current
// connection, required by the wallet handlers to work, and based on the
// permissions the client has set.
type ConnectedWallet struct {
// Hostname is the hostname for which the connection is set.
Hostname string
// Wallet is the wallet selected by the client for this connection.
Wallet wallet.Wallet
// RestrictedKeys holds the keys that have been selected by the client
// during the permissions request.
RestrictedKeys map[string]wallet.KeyPair
}
func (s *ConnectedWallet) Permissions() wallet.Permissions {
return s.Wallet.Permissions(s.Hostname)
}
// CanUseKey determines is the permissions allow the specified key to be used,
// and ensure the key exist and is not tainted.
func (s *ConnectedWallet) CanUseKey(pubKey string) bool {
if !s.Permissions().CanUseKey(pubKey) {
return false
}
kp, err := s.Wallet.DescribeKeyPair(pubKey)
if err != nil {
return false
}
return !kp.IsTainted()
}
func (s *ConnectedWallet) UpdatePermissions(perms wallet.Permissions) error {
if err := s.Wallet.UpdatePermissions(s.Hostname, perms); err != nil {
return fmt.Errorf("could not update permission on the wallet: %w", err)
}
// Since we just updated the permissions on the wallet, we need to reload
// the restricted keys to match the update.
if err := s.loadRestrictedKeys(); err != nil {
return err
}
return nil
}
func (s *ConnectedWallet) ReloadWithWallet(updatedWallet wallet.Wallet) error {
s.Wallet = updatedWallet
if err := s.loadRestrictedKeys(); err != nil {
return err
}
return nil
}
func (s *ConnectedWallet) loadRestrictedKeys() error {
perms := s.Permissions()
if !perms.PublicKeys.Enabled() {
return nil
}
if perms.PublicKeys.HasRestrictedKeys() {
for _, pubKey := range perms.PublicKeys.RestrictedKeys {
keyPair, err := s.Wallet.DescribeKeyPair(pubKey)
if err != nil {
return fmt.Errorf("could not load the key pair associated to the public key %q: %w", pubKey, err)
}
s.RestrictedKeys[keyPair.PublicKey()] = keyPair
}
return nil
}
// If there is no restricted keys set, we load all valid keys.
for _, keyPair := range s.Wallet.ListKeyPairs() {
if !keyPair.IsTainted() {
s.RestrictedKeys[keyPair.PublicKey()] = keyPair
}
}
return nil
}
func NewConnectedWallet(hostname string, w wallet.Wallet) (*ConnectedWallet, error) {
s := &ConnectedWallet{
Hostname: hostname,
Wallet: w,
RestrictedKeys: map[string]wallet.KeyPair{},
}
if err := s.loadRestrictedKeys(); err != nil {
return nil, fmt.Errorf("could not load the restricted keys: %w", err)
}
return s, nil
}
func toFingerprint(hostname string, w wallet.Wallet) string {
return vgcrypto.HashStr(hostname + "::" + w.Name())
}