taler-mailbox

Service for asynchronous wallet-to-wallet payment messages
Log | Files | Refs | Submodules | README | LICENSE

main_test.go (14457B)


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