Files
proton-bridge/internal/users/credentials/store_test.go

298 lines
7.4 KiB
Go

// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package credentials
import (
"bytes"
"encoding/base64"
"encoding/gob"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const testSep = "\n"
const secretFormat = "%v" + testSep + // UserID,
"%v" + testSep + // Name,
"%v" + testSep + // Emails,
"%v" + testSep + // APIToken,
"%v" + testSep + // Mailbox,
"%v" + testSep + // BridgePassword,
"%v" + testSep + // Version string
"%v" + testSep + // Timestamp,
"%v" + testSep + // IsHidden,
"%v" // IsCombinedAddressMode
// the best would be to run this test on mac, win, and linux separately
type testCredentials struct {
UserID,
Name,
Emails,
APIToken,
Mailbox,
BridgePassword,
Version string
Timestamp int64
IsHidden,
IsCombinedAddressMode bool
}
func init() { //nolint[gochecknoinits]
gob.Register(testCredentials{})
}
func (s *testCredentials) MarshalGob() string {
buf := bytes.Buffer{}
enc := gob.NewEncoder(&buf)
if err := enc.Encode(s); err != nil {
return ""
}
fmt.Printf("MarshalGob: %#v\n", buf.String())
return base64.StdEncoding.EncodeToString(buf.Bytes())
}
func (s *testCredentials) Clear() {
s.UserID = ""
s.Name = ""
s.Emails = ""
s.APIToken = ""
s.Mailbox = ""
s.BridgePassword = ""
s.Version = ""
s.Timestamp = 0
s.IsHidden = false
s.IsCombinedAddressMode = false
}
func (s *testCredentials) UnmarshalGob(secret string) error {
s.Clear()
b, err := base64.StdEncoding.DecodeString(secret)
if err != nil {
fmt.Println("decode base64", b)
return err
}
buf := bytes.NewBuffer(b)
dec := gob.NewDecoder(buf)
if err = dec.Decode(s); err != nil {
fmt.Println("decode gob", b, buf.Bytes())
return err
}
return nil
}
func (s *testCredentials) ToJSON() string {
if b, err := json.Marshal(s); err == nil {
fmt.Printf("MarshalJSON: %#v\n", string(b))
return base64.StdEncoding.EncodeToString(b)
}
return ""
}
func (s *testCredentials) FromJSON(secret string) error {
b, err := base64.StdEncoding.DecodeString(secret)
if err != nil {
return err
}
if err = json.Unmarshal(b, s); err == nil {
return nil
}
return err
}
func (s *testCredentials) MarshalFmt() string {
buf := bytes.Buffer{}
fmt.Fprintf(
&buf, secretFormat,
s.UserID,
s.Name,
s.Emails,
s.APIToken,
s.Mailbox,
s.BridgePassword,
s.Version,
s.Timestamp,
s.IsHidden,
s.IsCombinedAddressMode,
)
fmt.Printf("MarshalFmt: %#v\n", buf.String())
return base64.StdEncoding.EncodeToString(buf.Bytes())
}
func (s *testCredentials) UnmarshalFmt(secret string) error {
b, err := base64.StdEncoding.DecodeString(secret)
if err != nil {
return err
}
buf := bytes.NewBuffer(b)
fmt.Println("decode fmt", b, buf.Bytes())
_, err = fmt.Fscanf(
buf, secretFormat,
&s.UserID,
&s.Name,
&s.Emails,
&s.APIToken,
&s.Mailbox,
&s.BridgePassword,
&s.Version,
&s.Timestamp,
&s.IsHidden,
&s.IsCombinedAddressMode,
)
if err != nil {
return err
}
return nil
}
func (s *testCredentials) MarshalStrings() string { // this is the most space efficient
items := []string{
s.UserID, // 0
s.Name, // 1
s.Emails, // 2
s.APIToken, // 3
s.Mailbox, // 4
s.BridgePassword, // 5
s.Version, // 6
}
items = append(items, fmt.Sprint(s.Timestamp)) // 7
if s.IsHidden { // 8
items = append(items, "1")
} else {
items = append(items, "")
}
if s.IsCombinedAddressMode { // 9
items = append(items, "1")
} else {
items = append(items, "")
}
str := strings.Join(items, sep)
fmt.Printf("MarshalJoin: %#v\n", str)
return base64.StdEncoding.EncodeToString([]byte(str))
}
func (s *testCredentials) UnmarshalStrings(secret string) error {
b, err := base64.StdEncoding.DecodeString(secret)
if err != nil {
return err
}
items := strings.Split(string(b), sep)
if len(items) != 10 {
return ErrWrongFormat
}
s.UserID = items[0]
s.Name = items[1]
s.Emails = items[2]
s.APIToken = items[3]
s.Mailbox = items[4]
s.BridgePassword = items[5]
s.Version = items[6]
if _, err = fmt.Sscanf(items[7], "%d", &s.Timestamp); err != nil {
s.Timestamp = 0
}
if s.IsHidden = false; items[8] == "1" {
s.IsHidden = true
}
if s.IsCombinedAddressMode = false; items[9] == "1" {
s.IsCombinedAddressMode = true
}
return nil
}
func (s *testCredentials) IsSame(rhs *testCredentials) bool {
return s.Name == rhs.Name &&
s.Emails == rhs.Emails &&
s.APIToken == rhs.APIToken &&
s.Mailbox == rhs.Mailbox &&
s.BridgePassword == rhs.BridgePassword &&
s.Version == rhs.Version &&
s.Timestamp == rhs.Timestamp &&
s.IsHidden == rhs.IsHidden &&
s.IsCombinedAddressMode == rhs.IsCombinedAddressMode
}
func TestMarshalFormats(t *testing.T) {
input := testCredentials{UserID: "007", Emails: "ja@pm.me;jakub@cu.th", Timestamp: 152469263742, IsHidden: true}
fmt.Printf("input %#v\n", input)
secretStrings := input.MarshalStrings()
fmt.Printf("secretStrings %#v %d\n", secretStrings, len(secretStrings))
secretGob := input.MarshalGob()
fmt.Printf("secretGob %#v %d\n", secretGob, len(secretGob))
secretJSON := input.ToJSON()
fmt.Printf("secretJSON %#v %d\n", secretJSON, len(secretJSON))
secretFmt := input.MarshalFmt()
fmt.Printf("secretFmt %#v %d\n", secretFmt, len(secretFmt))
output := testCredentials{APIToken: "refresh"}
require.NoError(t, output.UnmarshalStrings(secretStrings))
fmt.Printf("strings out %#v \n", output)
require.True(t, input.IsSame(&output), "strings out not same")
output = testCredentials{APIToken: "refresh"}
require.NoError(t, output.UnmarshalGob(secretGob))
fmt.Printf("gob out %#v\n \n", output)
assert.Equal(t, input, output)
output = testCredentials{APIToken: "refresh"}
require.NoError(t, output.FromJSON(secretJSON))
fmt.Printf("json out %#v \n", output)
require.True(t, input.IsSame(&output), "json out not same")
/*
// Simple Fscanf not working!
output = testCredentials{APIToken: "refresh"}
require.NoError(t, output.UnmarshalFmt(secretFmt))
fmt.Printf("fmt out %#v \n", output)
require.True(t, input.IsSame(&output), "fmt out not same")
*/
}
func TestMarshal(t *testing.T) {
input := Credentials{
UserID: "",
Name: "007",
Emails: "ja@pm.me;aj@cus.tom",
APIToken: "sdfdsfsdfsdfsdf",
MailboxPassword: "cdcdcdcd",
BridgePassword: "wew123",
Version: "k11",
Timestamp: 152469263742,
IsHidden: true,
IsCombinedAddressMode: false,
}
fmt.Printf("input %#v\n", input)
secret := input.Marshal()
fmt.Printf("secret %#v %d\n", secret, len(secret))
output := Credentials{APIToken: "refresh"}
require.NoError(t, output.Unmarshal(secret))
fmt.Printf("output %#v\n", output)
assert.Equal(t, input, output)
}