github.com/MetalBlockchain/metalgo@v1.11.9/api/keystore/service_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package keystore 5 6 import ( 7 "encoding/hex" 8 "math/rand" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 13 "github.com/MetalBlockchain/metalgo/api" 14 "github.com/MetalBlockchain/metalgo/database/memdb" 15 "github.com/MetalBlockchain/metalgo/ids" 16 "github.com/MetalBlockchain/metalgo/utils/formatting" 17 "github.com/MetalBlockchain/metalgo/utils/logging" 18 "github.com/MetalBlockchain/metalgo/utils/password" 19 ) 20 21 // strongPassword defines a password used for the following tests that 22 // scores high enough to pass the password strength scoring system 23 var strongPassword = "N_+=_jJ;^(<;{4,:*m6CET}'&N;83FYK.wtNpwp-Jt" // #nosec G101 24 25 func TestServiceListNoUsers(t *testing.T) { 26 require := require.New(t) 27 28 ks := New(logging.NoLog{}, memdb.New()) 29 s := service{ks: ks.(*keystore)} 30 31 reply := ListUsersReply{} 32 require.NoError(s.ListUsers(nil, nil, &reply)) 33 require.Empty(reply.Users) 34 } 35 36 func TestServiceCreateUser(t *testing.T) { 37 require := require.New(t) 38 39 ks := New(logging.NoLog{}, memdb.New()) 40 s := service{ks: ks.(*keystore)} 41 42 { 43 require.NoError(s.CreateUser(nil, &api.UserPass{ 44 Username: "bob", 45 Password: strongPassword, 46 }, &api.EmptyReply{})) 47 } 48 49 { 50 reply := ListUsersReply{} 51 require.NoError(s.ListUsers(nil, nil, &reply)) 52 require.Len(reply.Users, 1) 53 require.Equal("bob", reply.Users[0]) 54 } 55 } 56 57 // genStr returns a string of given length 58 func genStr(n int) string { 59 b := make([]byte, n) 60 rand.Read(b) // #nosec G404 61 return hex.EncodeToString(b)[:n] 62 } 63 64 // TestServiceCreateUserArgsCheck generates excessively long usernames or 65 // passwords to assure the sanity checks on string length are not exceeded 66 func TestServiceCreateUserArgsCheck(t *testing.T) { 67 require := require.New(t) 68 69 ks := New(logging.NoLog{}, memdb.New()) 70 s := service{ks: ks.(*keystore)} 71 72 { 73 reply := api.EmptyReply{} 74 err := s.CreateUser(nil, &api.UserPass{ 75 Username: genStr(maxUserLen + 1), 76 Password: strongPassword, 77 }, &reply) 78 require.ErrorIs(err, errUserMaxLength) 79 } 80 81 { 82 reply := api.EmptyReply{} 83 err := s.CreateUser(nil, &api.UserPass{ 84 Username: "shortuser", 85 Password: genStr(maxUserLen + 1), 86 }, &reply) 87 require.ErrorIs(err, password.ErrPassMaxLength) 88 } 89 90 { 91 reply := ListUsersReply{} 92 require.NoError(s.ListUsers(nil, nil, &reply)) 93 require.Empty(reply.Users) 94 } 95 } 96 97 // TestServiceCreateUserWeakPassword tests creating a new user with a weak 98 // password to ensure the password strength check is working 99 func TestServiceCreateUserWeakPassword(t *testing.T) { 100 require := require.New(t) 101 102 ks := New(logging.NoLog{}, memdb.New()) 103 s := service{ks: ks.(*keystore)} 104 105 { 106 reply := api.EmptyReply{} 107 err := s.CreateUser(nil, &api.UserPass{ 108 Username: "bob", 109 Password: "weak", 110 }, &reply) 111 require.ErrorIs(err, password.ErrWeakPassword) 112 } 113 } 114 115 func TestServiceCreateDuplicate(t *testing.T) { 116 require := require.New(t) 117 118 ks := New(logging.NoLog{}, memdb.New()) 119 s := service{ks: ks.(*keystore)} 120 121 { 122 require.NoError(s.CreateUser(nil, &api.UserPass{ 123 Username: "bob", 124 Password: strongPassword, 125 }, &api.EmptyReply{})) 126 } 127 128 { 129 err := s.CreateUser(nil, &api.UserPass{ 130 Username: "bob", 131 Password: strongPassword, 132 }, &api.EmptyReply{}) 133 require.ErrorIs(err, errUserAlreadyExists) 134 } 135 } 136 137 func TestServiceCreateUserNoName(t *testing.T) { 138 require := require.New(t) 139 140 ks := New(logging.NoLog{}, memdb.New()) 141 s := service{ks: ks.(*keystore)} 142 143 reply := api.EmptyReply{} 144 err := s.CreateUser(nil, &api.UserPass{ 145 Password: strongPassword, 146 }, &reply) 147 require.ErrorIs(err, errEmptyUsername) 148 } 149 150 func TestServiceUseBlockchainDB(t *testing.T) { 151 require := require.New(t) 152 153 ks := New(logging.NoLog{}, memdb.New()) 154 s := service{ks: ks.(*keystore)} 155 156 { 157 require.NoError(s.CreateUser(nil, &api.UserPass{ 158 Username: "bob", 159 Password: strongPassword, 160 }, &api.EmptyReply{})) 161 } 162 163 { 164 db, err := ks.GetDatabase(ids.Empty, "bob", strongPassword) 165 require.NoError(err) 166 require.NoError(db.Put([]byte("hello"), []byte("world"))) 167 } 168 169 { 170 db, err := ks.GetDatabase(ids.Empty, "bob", strongPassword) 171 require.NoError(err) 172 val, err := db.Get([]byte("hello")) 173 require.NoError(err) 174 require.Equal([]byte("world"), val) 175 } 176 } 177 178 func TestServiceExportImport(t *testing.T) { 179 require := require.New(t) 180 181 encodings := []formatting.Encoding{formatting.Hex} 182 for _, encoding := range encodings { 183 ks := New(logging.NoLog{}, memdb.New()) 184 s := service{ks: ks.(*keystore)} 185 186 { 187 require.NoError(s.CreateUser(nil, &api.UserPass{ 188 Username: "bob", 189 Password: strongPassword, 190 }, &api.EmptyReply{})) 191 } 192 193 { 194 db, err := ks.GetDatabase(ids.Empty, "bob", strongPassword) 195 require.NoError(err) 196 require.NoError(db.Put([]byte("hello"), []byte("world"))) 197 } 198 199 exportArgs := ExportUserArgs{ 200 UserPass: api.UserPass{ 201 Username: "bob", 202 Password: strongPassword, 203 }, 204 Encoding: encoding, 205 } 206 exportReply := ExportUserReply{} 207 require.NoError(s.ExportUser(nil, &exportArgs, &exportReply)) 208 209 newKS := New(logging.NoLog{}, memdb.New()) 210 newS := service{ks: newKS.(*keystore)} 211 212 { 213 err := newS.ImportUser(nil, &ImportUserArgs{ 214 UserPass: api.UserPass{ 215 Username: "bob", 216 Password: "", 217 }, 218 User: exportReply.User, 219 }, &api.EmptyReply{}) 220 require.ErrorIs(err, errIncorrectPassword) 221 } 222 223 { 224 err := newS.ImportUser(nil, &ImportUserArgs{ 225 UserPass: api.UserPass{ 226 Username: "", 227 Password: "strongPassword", 228 }, 229 User: exportReply.User, 230 }, &api.EmptyReply{}) 231 require.ErrorIs(err, errEmptyUsername) 232 } 233 234 { 235 require.NoError(newS.ImportUser(nil, &ImportUserArgs{ 236 UserPass: api.UserPass{ 237 Username: "bob", 238 Password: strongPassword, 239 }, 240 User: exportReply.User, 241 Encoding: encoding, 242 }, &api.EmptyReply{})) 243 } 244 245 { 246 db, err := newKS.GetDatabase(ids.Empty, "bob", strongPassword) 247 require.NoError(err) 248 val, err := db.Get([]byte("hello")) 249 require.NoError(err) 250 require.Equal([]byte("world"), val) 251 } 252 } 253 } 254 255 func TestServiceDeleteUser(t *testing.T) { 256 testUser := "testUser" 257 password := "passwTest@fake01ord" 258 tests := []struct { 259 desc string 260 setup func(ks *keystore) error 261 request *api.UserPass 262 want *api.EmptyReply 263 expectedErr error 264 }{ 265 { 266 desc: "empty user name case", 267 request: &api.UserPass{}, 268 expectedErr: errEmptyUsername, 269 }, 270 { 271 desc: "user not exists case", 272 request: &api.UserPass{Username: "dummy"}, 273 expectedErr: errNonexistentUser, 274 }, 275 { 276 desc: "user exists and invalid password case", 277 setup: func(ks *keystore) error { 278 s := service{ks: ks} 279 return s.CreateUser(nil, &api.UserPass{Username: testUser, Password: password}, &api.EmptyReply{}) 280 }, 281 request: &api.UserPass{Username: testUser, Password: "password"}, 282 expectedErr: errIncorrectPassword, 283 }, 284 { 285 desc: "user exists and valid password case", 286 setup: func(ks *keystore) error { 287 s := service{ks: ks} 288 return s.CreateUser(nil, &api.UserPass{Username: testUser, Password: password}, &api.EmptyReply{}) 289 }, 290 request: &api.UserPass{Username: testUser, Password: password}, 291 want: &api.EmptyReply{}, 292 }, 293 { 294 desc: "delete a user, imported from import api case", 295 setup: func(ks *keystore) error { 296 s := service{ks: ks} 297 298 reply := api.EmptyReply{} 299 if err := s.CreateUser(nil, &api.UserPass{Username: testUser, Password: password}, &reply); err != nil { 300 return err 301 } 302 303 // created data in bob db 304 db, err := ks.GetDatabase(ids.Empty, testUser, password) 305 if err != nil { 306 return err 307 } 308 309 return db.Put([]byte("hello"), []byte("world")) 310 }, 311 request: &api.UserPass{Username: testUser, Password: password}, 312 want: &api.EmptyReply{}, 313 }, 314 } 315 316 for _, tt := range tests { 317 t.Run(tt.desc, func(t *testing.T) { 318 require := require.New(t) 319 320 ksIntf := New(logging.NoLog{}, memdb.New()) 321 ks := ksIntf.(*keystore) 322 s := service{ks: ks} 323 324 if tt.setup != nil { 325 require.NoError(tt.setup(ks)) 326 } 327 got := &api.EmptyReply{} 328 err := s.DeleteUser(nil, tt.request, got) 329 require.ErrorIs(err, tt.expectedErr) 330 if tt.expectedErr != nil { 331 return 332 } 333 require.Equal(tt.want, got) 334 require.NotContains(ks.usernameToPassword, testUser) // delete is successful 335 336 // deleted user details should be available to create user again. 337 require.NoError(s.CreateUser(nil, &api.UserPass{Username: testUser, Password: password}, &api.EmptyReply{})) 338 }) 339 } 340 }