-
Notifications
You must be signed in to change notification settings - Fork 22
/
connected_wallet.go
194 lines (163 loc) · 5.39 KB
/
connected_wallet.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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// Copyright (C) 2023 Gobalsky Labs Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package api
import (
"fmt"
"code.vegaprotocol.io/vega/wallet/wallet"
)
// ConnectedWallet is the projection of the wallet through the permissions
// and authentication system. On a regular wallet, there are no restrictions
// on what we can call, which doesn't fit the model of having allowed
// access, so we wrap the "regular wallet" behind the "connected wallet".
type ConnectedWallet struct {
// name is the name of the wallet.
name string
// Hostname is the hostname for which the connection is set.
hostname string
// allowedKeys holds the keys that have been selected by the client
// during the permissions request.
// The order should match the order of generation in the wallet.
allowedKeys []AllowedKey
// noRestrictions is a hack to know if we should skip permission
// verification when we are connected with a long-living API token.
noRestrictions bool
canListKeys bool
}
func (s *ConnectedWallet) Name() string {
return s.name
}
// Hostname returns the hostname for which the connection has been set.
// For long-living connections, the hostname is empty as there is no
// restrictions for that type of connection.
func (s *ConnectedWallet) Hostname() string {
return s.hostname
}
// AllowedKeys returns the keys a connection has access to. If a third-party
// application tries to use a keys that does not belong to this set, then the
// request should fail.
func (s *ConnectedWallet) AllowedKeys() []AllowedKey {
return s.allowedKeys
}
// RequireInteraction tells if an interaction with the user is needed for
// supervision is required or not.
// It is related to the type of API token that is used for this connection.
// If it's a long-living token, then no interaction is required.
func (s *ConnectedWallet) RequireInteraction() bool {
return !s.noRestrictions
}
func (s *ConnectedWallet) CanListKeys() bool {
if s.noRestrictions {
return true
}
return s.canListKeys
}
// CanUseKey determines if the permissions allow the specified key to be used.
func (s *ConnectedWallet) CanUseKey(publicKeyToUse string) bool {
for _, allowedKey := range s.allowedKeys {
if allowedKey.PublicKey() == publicKeyToUse {
return true
}
}
return false
}
func (s *ConnectedWallet) RefreshFromWallet(freshWallet wallet.Wallet) error {
if s.noRestrictions {
s.allowedKeys = allUsableKeys(freshWallet)
return nil
}
rks, err := allowedKeys(freshWallet, s.hostname)
if err != nil {
return fmt.Errorf("could not resolve the allowed keys when refreshing the connection: %w", err)
}
s.canListKeys = rks != nil
s.allowedKeys = rks
return nil
}
type AllowedKey struct {
publicKey string
name string
}
func (r AllowedKey) PublicKey() string {
return r.publicKey
}
func (r AllowedKey) Name() string {
return r.name
}
func NewDisconnectedWallet(hostname, wallet string) ConnectedWallet {
return ConnectedWallet{
name: wallet,
hostname: hostname,
}
}
func NewConnectedWallet(hostname string, w wallet.Wallet) (ConnectedWallet, error) {
rks, err := allowedKeys(w, hostname)
if err != nil {
return ConnectedWallet{}, fmt.Errorf("could not resolve the allowed keys: %w", err)
}
return ConnectedWallet{
noRestrictions: false,
canListKeys: rks != nil,
allowedKeys: rks,
hostname: hostname,
name: w.Name(),
}, nil
}
func NewLongLivingConnectedWallet(w wallet.Wallet) ConnectedWallet {
return ConnectedWallet{
noRestrictions: true,
canListKeys: true,
allowedKeys: allUsableKeys(w),
hostname: "",
name: w.Name(),
}
}
func allowedKeys(w wallet.Wallet, hostname string) ([]AllowedKey, error) {
perms := w.Permissions(hostname)
if !perms.PublicKeys.Enabled() {
return nil, nil
}
if !perms.PublicKeys.HasAllowedKeys() {
// If there is no allowed keys set for this hostname, we load all valid
// keys.
return allUsableKeys(w), nil
}
allowedKeys := make([]AllowedKey, 0, len(perms.PublicKeys.AllowedKeys))
for _, pubKey := range perms.PublicKeys.AllowedKeys {
keyPair, err := w.DescribeKeyPair(pubKey)
if err != nil {
return nil, fmt.Errorf("could not load the key pair associated to the public key %q: %w", pubKey, err)
}
// There is no need to check for the tainted keys, here, as this list
// should only contain usable keys.
allowedKeys = append(allowedKeys, AllowedKey{
publicKey: keyPair.PublicKey(),
name: keyPair.Name(),
})
}
return allowedKeys, nil
}
func allUsableKeys(w wallet.Wallet) []AllowedKey {
allKeyPairs := w.ListKeyPairs()
allowedKeys := make([]AllowedKey, 0, len(allKeyPairs))
for _, keyPair := range allKeyPairs {
if !keyPair.IsTainted() {
allowedKeys = append(allowedKeys, AllowedKey{
publicKey: keyPair.PublicKey(),
name: keyPair.Name(),
})
}
}
return allowedKeys
}