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