main_test.go (14905B)
1 package main_test 2 3 import ( 4 "bytes" 5 "crypto/ed25519" 6 "crypto/rand" 7 "crypto/sha512" 8 "database/sql" 9 "encoding/binary" 10 "encoding/json" 11 "fmt" 12 "log" 13 "net/http" 14 "net/http/httptest" 15 "os" 16 "strconv" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/schanzen/taler-go/pkg/merchant" 22 talerutil "github.com/schanzen/taler-go/pkg/util" 23 "gopkg.in/ini.v1" 24 "taler.net/taler-mailbox/internal/gana" 25 "taler.net/taler-mailbox/internal/util" 26 "taler.net/taler-mailbox/pkg/rest" 27 ) 28 29 var a mailbox.Mailbox 30 31 var testAliceSigningKeyPriv ed25519.PrivateKey 32 var testAliceSigningKey ed25519.PublicKey 33 var testAliceHashedSigningKeyString string 34 35 const merchantConfigResponse = `{ 36 "currency": "KUDOS", 37 "currencies": { 38 "KUDOS": { 39 "name": "Kudos (Taler Demonstrator)", 40 "currency": "KUDOS", 41 "num_fractional_input_digits": 2, 42 "num_fractional_normal_digits": 2, 43 "num_fractional_trailing_zero_digits": 2, 44 "alt_unit_names": { 45 "0": "ク" 46 } 47 } 48 }, 49 "exchanges": [ 50 { 51 "master_pub": "F80MFRG8HVH6R9CQ47KRFQSJP3T6DBJ4K1D9B703RJY3Z39TBMJ0", 52 "currency": "KUDOS", 53 "base_url": "https://exchange.demo.taler.net/" 54 } 55 ], 56 "implementation": "urn:net:taler:specs:taler-merchant:c-reference", 57 "name": "taler-merchant", 58 "version": "18:0:15" 59 }` 60 61 func executeRequest(req *http.Request) *httptest.ResponseRecorder { 62 rr := httptest.NewRecorder() 63 a.Router.ServeHTTP(rr, req) 64 return rr 65 } 66 67 func checkResponseCode(t *testing.T, expected, actual int) bool { 68 if expected != actual { 69 t.Errorf("Expected response code %d, Got %d\n", expected, actual) 70 } 71 return expected == actual 72 } 73 74 var merchServerRespondsPaid = false 75 76 func shouldReturnPaid() bool { 77 return merchServerRespondsPaid 78 } 79 80 func TestMain(m *testing.M) { 81 cfg, err := ini.Load("test-mailbox.conf") 82 if err != nil { 83 fmt.Printf("Failed to read config: %v", err) 84 os.Exit(1) 85 } 86 psqlconn := cfg.Section("mailbox-pq").Key("connection_string").MustString("postgres:///taler-mailbox") 87 segments := strings.Split(strings.Split(psqlconn, "?")[0], "/") 88 dbName := segments[len(segments)-1] 89 90 db, err := sql.Open("postgres", psqlconn) 91 if err != nil { 92 log.Panic(err) 93 } 94 defer db.Close() 95 err = talerutil.DBInit(db, "../..", dbName, "taler-mailbox") 96 if err != nil { 97 log.Fatalf("Failed to apply versioning or patches: %v", err) 98 } 99 merchServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 100 var orderResp merchant.PostOrderRequest 101 if r.URL.Path == "/config" { 102 w.WriteHeader(http.StatusOK) 103 w.Write([]byte(merchantConfigResponse)) 104 return 105 } 106 if !strings.HasPrefix(r.URL.Path, "/private/orders") { 107 fmt.Printf("Expected to request '/private/orders', got: %s\n", r.URL.Path) 108 return 109 } 110 if r.Method == http.MethodPost { 111 err := json.NewDecoder(r.Body).Decode(&orderResp) 112 if err != nil { 113 fmt.Printf("Error %s\n", err) 114 } 115 jsonResp := fmt.Sprintf("{\"order_id\":\"%s\"}", "uniqueOrderId") 116 w.WriteHeader(http.StatusOK) 117 w.Write([]byte(jsonResp)) 118 } else { 119 fmt.Printf("Responding always paid: %v\n", merchServerRespondsPaid) 120 if shouldReturnPaid() { 121 jsonResp := "{\"order_status\":\"paid\"}" 122 w.WriteHeader(http.StatusOK) 123 w.Write([]byte(jsonResp)) 124 } else { 125 jsonResp := "{\"order_status\":\"unpaid\", \"taler_pay_uri\": \"somepaytouri\"}" 126 w.WriteHeader(http.StatusOK) 127 w.Write([]byte(jsonResp)) 128 } 129 } 130 })) 131 defer merchServer.Close() 132 merch := merchant.NewMerchant(merchServer.URL, "supersecret") 133 a.Initialize(mailbox.MailboxConfig{ 134 Version: "testing", 135 DB: db, 136 Merchant: merch, 137 Ini: cfg}) 138 testAliceSigningKey, testAliceSigningKeyPriv, _ = ed25519.GenerateKey(nil) 139 h := sha512.New() 140 h.Write(testAliceSigningKey) 141 testAliceHashedSigningKeyString = util.Base32CrockfordEncode(h.Sum(nil)) 142 143 a.Merchant = merchant.NewMerchant(merchServer.URL, "") 144 145 code := m.Run() 146 // Purge DB 147 mailbox.DeleteAllInboxEntriesFromDatabase(a.DB) 148 os.Exit(code) 149 } 150 151 func TestEmptyMailbox(t *testing.T) { 152 mailbox.DeleteAllInboxEntriesFromDatabase(a.DB) 153 req, _ := http.NewRequest("GET", "/"+testAliceHashedSigningKeyString, nil) 154 response := executeRequest(req) 155 156 checkResponseCode(t, http.StatusNoContent, response.Code) 157 158 body := response.Body.String() 159 if body != "" { 160 t.Errorf("Expected empty response, Got %s", body) 161 } 162 } 163 164 func TestSendMessage(t *testing.T) { 165 testMessage := make([]byte, 256) 166 mailbox.DeleteAllInboxEntriesFromDatabase(a.DB) 167 req, _ := http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage)) 168 response := executeRequest(req) 169 170 checkResponseCode(t, http.StatusNoContent, response.Code) 171 172 body := response.Body.String() 173 if body != "" { 174 t.Errorf("Expected empty response, Got %s", body) 175 } 176 } 177 178 func setMailboxPaid(isPaid bool) { 179 var messageFee talerutil.Amount 180 if isPaid { 181 messageFee = talerutil.NewAmount("KUDOS", 1, 0) 182 } else { 183 messageFee = talerutil.NewAmount("KUDOS", 0, 0) 184 } 185 a.MessageFee = &messageFee 186 a.FreeMessageQuota = 1 187 } 188 189 func TestSendMessagePaid(t *testing.T) { 190 191 // Make paid 192 setMailboxPaid(true) 193 194 // Cleanup 195 mailbox.DeleteAllInboxEntriesFromDatabase(a.DB) 196 197 testMessage := make([]byte, 256) 198 rand.Read(testMessage) 199 req, _ := http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage)) 200 response := executeRequest(req) 201 202 checkResponseCode(t, http.StatusNoContent, response.Code) 203 204 body := response.Body.String() 205 if body != "" { 206 t.Errorf("Expected empty response, Got %s", body) 207 } 208 testMessage2 := make([]byte, 256) 209 rand.Read(testMessage2) 210 req, _ = http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage2)) 211 response = executeRequest(req) 212 213 checkResponseCode(t, http.StatusPaymentRequired, response.Code) 214 215 body = response.Body.String() 216 if body != "" { 217 t.Errorf("Expected empty response, Got %s", body) 218 } 219 setMailboxPaid(false) 220 } 221 222 func TestGetKeysEmpty(t *testing.T) { 223 req, _ := http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 224 response := executeRequest(req) 225 checkResponseCode(t, http.StatusNotFound, response.Code) 226 } 227 228 func TestMailboxRegistration(t *testing.T) { 229 var msg mailbox.MailboxRegistrationRequest 230 // Dummy pubkey 231 encKey := make([]byte, 32) 232 aliceSigningKey := util.Base32CrockfordEncode(testAliceSigningKey) 233 msg.MailboxMetadata.EncryptionKey = util.Base32CrockfordEncode(encKey) 234 msg.MailboxMetadata.EncryptionKeyType = "X25519" 235 msg.MailboxMetadata.Expiration.Seconds = uint64(time.Now().Add(time.Hour * 24 * 365).Unix()) 236 msg.MailboxMetadata.SigningKey = aliceSigningKey 237 msg.MailboxMetadata.SigningKeyType = "EdDSA" 238 expNbo := make([]byte, 8) 239 binary.BigEndian.PutUint64(expNbo, msg.MailboxMetadata.Expiration.Seconds) 240 h := sha512.New() 241 h.Write([]byte(msg.MailboxMetadata.EncryptionKeyType)) 242 h.Write(encKey) 243 h.Write(expNbo) 244 var signedMsg [64 + 4 + 4]byte 245 size := signedMsg[0:4] 246 binary.BigEndian.PutUint32(size, 64+4+4) 247 purp := signedMsg[4:8] 248 binary.BigEndian.PutUint32(purp, gana.TalerSignaturePurposeMailboxRegister) 249 copy(signedMsg[8:], h.Sum(nil)) 250 sig := ed25519.Sign(testAliceSigningKeyPriv, signedMsg[0:]) 251 if !ed25519.Verify(testAliceSigningKey, signedMsg[0:], sig) { 252 t.Errorf("Signature invalid!") 253 } 254 msg.Signature = util.Base32CrockfordEncode(sig) 255 jsonMsg, _ := json.Marshal(msg) 256 req, _ := http.NewRequest("POST", "/register", bytes.NewReader(jsonMsg)) 257 response := executeRequest(req) 258 checkResponseCode(t, http.StatusNoContent, response.Code) 259 req, _ = http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 260 response = executeRequest(req) 261 checkResponseCode(t, http.StatusOK, response.Code) 262 body := response.Body.String() 263 if body == "" { 264 t.Errorf("Expected response, Got %s", body) 265 return 266 } 267 var respMsg mailbox.MailboxMetadata 268 err := json.NewDecoder(response.Body).Decode(&respMsg) 269 fmt.Println(respMsg) 270 if err != nil { 271 t.Errorf("Error %s\n", err) 272 } 273 if respMsg.SigningKey != msg.MailboxMetadata.SigningKey { 274 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 275 } 276 if respMsg.EncryptionKey != msg.MailboxMetadata.EncryptionKey { 277 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 278 } 279 mailbox.DeleteAllMailboxesFromDatabase(a.DB) 280 mailbox.DeleteAllPendingRegistrationsFromDatabase(a.DB) 281 } 282 283 func createMailboxMetadata(encKey []byte, signKey []byte, info string) mailbox.MailboxMetadata { 284 var mb mailbox.MailboxMetadata 285 aliceSigningKey := util.Base32CrockfordEncode(signKey) 286 mb.EncryptionKey = util.Base32CrockfordEncode(encKey) 287 mb.EncryptionKeyType = "X25519" 288 mb.Expiration.Seconds = uint64(time.Now().Add(time.Hour * 24 * 365).Unix()) 289 mb.SigningKey = aliceSigningKey 290 mb.SigningKeyType = "EdDSA" 291 if info != "" { 292 mb.Info = info 293 } 294 return mb 295 } 296 297 func createMailboxRegistrationMessage(encKey []byte, md mailbox.MailboxMetadata) mailbox.MailboxRegistrationRequest { 298 var msg mailbox.MailboxRegistrationRequest 299 msg.MailboxMetadata = md 300 expNbo := make([]byte, 8) 301 binary.BigEndian.PutUint64(expNbo, msg.MailboxMetadata.Expiration.Seconds) 302 h := sha512.New() 303 h.Write([]byte(msg.MailboxMetadata.EncryptionKeyType)) 304 h.Write(encKey) 305 h.Write(expNbo) 306 var signedMsg [64 + 4 + 4]byte 307 size := signedMsg[0:4] 308 binary.BigEndian.PutUint32(size, 64+4+4) 309 purp := signedMsg[4:8] 310 binary.BigEndian.PutUint32(purp, gana.TalerSignaturePurposeMailboxRegister) 311 copy(signedMsg[8:], h.Sum(nil)) 312 sig := ed25519.Sign(testAliceSigningKeyPriv, signedMsg[0:]) 313 msg.Signature = util.Base32CrockfordEncode(sig) 314 return msg 315 } 316 317 func TestMailboxRegistrationWithInfo(t *testing.T) { 318 var msg mailbox.MailboxRegistrationRequest 319 encKey := make([]byte, 32) 320 md := createMailboxMetadata(encKey, testAliceSigningKey, "") 321 msg = createMailboxRegistrationMessage(encKey, md) 322 jsonMsg, _ := json.Marshal(msg) 323 req, _ := http.NewRequest("POST", "/register", bytes.NewReader(jsonMsg)) 324 response := executeRequest(req) 325 checkResponseCode(t, http.StatusNoContent, response.Code) 326 req, _ = http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 327 response = executeRequest(req) 328 checkResponseCode(t, http.StatusOK, response.Code) 329 body := response.Body.String() 330 if body == "" { 331 t.Errorf("Expected response, Got %s", body) 332 return 333 } 334 var respMsg mailbox.MailboxMetadata 335 err := json.NewDecoder(response.Body).Decode(&respMsg) 336 if err != nil { 337 t.Errorf("Error %s\n", err) 338 } 339 if respMsg.SigningKey != msg.MailboxMetadata.SigningKey { 340 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 341 } 342 if respMsg.EncryptionKey != msg.MailboxMetadata.EncryptionKey { 343 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 344 } 345 if respMsg.Info != "Hello World" { 346 fmt.Printf("Info field missing! %v %v\n", respMsg, msg.MailboxMetadata) 347 } 348 mailbox.DeleteAllMailboxesFromDatabase(a.DB) 349 mailbox.DeleteAllPendingRegistrationsFromDatabase(a.DB) 350 } 351 352 func TestMailboxRegistrationPaid(t *testing.T) { 353 var msg mailbox.MailboxRegistrationRequest 354 355 // Make paid 356 registrationUpdateFee := talerutil.NewAmount("KUDOS", 1, 0) 357 monthlyFee := talerutil.NewAmount("KUDOS", 2, 0) 358 a.RegistrationUpdateFee = ®istrationUpdateFee 359 a.MonthlyFee = &monthlyFee 360 361 // Dummy pubkey 362 encKey := make([]byte, 32) 363 md := createMailboxMetadata(encKey, testAliceSigningKey, "") 364 msg = createMailboxRegistrationMessage(encKey, md) 365 jsonMsg, _ := json.Marshal(msg) 366 req, _ := http.NewRequest("POST", "/register", bytes.NewReader(jsonMsg)) 367 response := executeRequest(req) 368 checkResponseCode(t, http.StatusPaymentRequired, response.Code) 369 370 req, _ = http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 371 response = executeRequest(req) 372 checkResponseCode(t, http.StatusNotFound, response.Code) 373 374 merchServerRespondsPaid = true 375 req, _ = http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 376 response = executeRequest(req) 377 checkResponseCode(t, http.StatusOK, response.Code) 378 merchServerRespondsPaid = false 379 380 body := response.Body.String() 381 if body == "" { 382 t.Errorf("Expected response, Got %s", body) 383 return 384 } 385 var respMsg mailbox.MailboxMetadata 386 err := json.NewDecoder(response.Body).Decode(&respMsg) 387 if err != nil { 388 fmt.Printf("Error %s\n", err) 389 } 390 if respMsg.SigningKey != msg.MailboxMetadata.SigningKey { 391 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 392 } 393 if respMsg.EncryptionKey != msg.MailboxMetadata.EncryptionKey { 394 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 395 } 396 } 397 398 func TestPostThenDeleteMessage(t *testing.T) { 399 400 // make not paid 401 numMessagesToPost := (a.MessageResponseLimit + 7) 402 testMessages := make([]byte, 256*numMessagesToPost) 403 _, _ = rand.Read(testMessages) 404 mailbox.DeleteAllInboxEntriesFromDatabase(a.DB) 405 406 for i := 0; i < int(numMessagesToPost); i++ { 407 testMessage := testMessages[i*256 : (i+1)*256] 408 req, _ := http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage)) 409 response := executeRequest(req) 410 411 checkResponseCode(t, http.StatusNoContent, response.Code) 412 413 body := response.Body.String() 414 if body != "" { 415 t.Errorf("Expected empty response, Got %s", body) 416 } 417 } 418 419 req, _ := http.NewRequest("GET", "/"+testAliceHashedSigningKeyString, nil) 420 response := executeRequest(req) 421 422 checkResponseCode(t, http.StatusOK, response.Code) 423 424 if response.Body.Len() != int(256*a.MessageResponseLimit) { 425 t.Errorf("Expected response of 25600 bytes, Got %d", response.Body.Len()) 426 } 427 428 etag := response.Result().Header.Get("ETag") 429 430 if etag == "" { 431 t.Errorf("ETag missing!\n") 432 } 433 434 // Now delete 10 messages 435 h := sha512.New() 436 for i := 0; i < int(a.MessageResponseLimit); i++ { 437 h.Write(testMessages[i*256 : (i+1)*256]) 438 } 439 etagInt, _ := strconv.Atoi(etag) 440 var signedMsg [4 * 4]byte 441 binary.BigEndian.PutUint32(signedMsg[0:4], 4*4) 442 binary.BigEndian.PutUint32(signedMsg[4:8], gana.TalerSignaturePurposeMailboxMessagesDelete) 443 binary.BigEndian.PutUint32(signedMsg[8:12], uint32(etagInt)) 444 binary.BigEndian.PutUint32(signedMsg[12:16], uint32(a.MessageResponseLimit)) 445 sig := ed25519.Sign(testAliceSigningKeyPriv, signedMsg[0:]) 446 if !ed25519.Verify(testAliceSigningKey, signedMsg[0:], sig) { 447 t.Errorf("Signature invalid!") 448 } 449 hAddress := util.Base32CrockfordEncode(testAliceSigningKey) 450 req, _ = http.NewRequest("DELETE", "/"+hAddress+"?count="+strconv.Itoa(int(a.MessageResponseLimit)), nil) 451 req.Header.Add("If-Match", etag) 452 req.Header.Add("Taler-Mailbox-Delete-Signature", util.Base32CrockfordEncode(sig)) 453 response = executeRequest(req) 454 455 checkResponseCode(t, http.StatusNoContent, response.Code) 456 457 body := response.Body.String() 458 if body != "" { 459 t.Errorf("Expected empty response, Got %s", body) 460 } 461 462 req, _ = http.NewRequest("GET", "/"+testAliceHashedSigningKeyString, nil) 463 response = executeRequest(req) 464 465 checkResponseCode(t, http.StatusOK, response.Code) 466 467 if response.Body.Len() != int(256*7) { 468 t.Errorf("Expected response of 256*7 bytes, Got %d", response.Body.Len()) 469 } 470 }