mirror of
https://github.com/ProtonMail/go-proton-api.git
synced 2025-12-23 23:57:50 -05:00
test(GODT-2181): Handle quark commands in test server
This commit is contained in:
18
address.go
18
address.go
@@ -44,3 +44,21 @@ func (c *Client) OrderAddresses(ctx context.Context, req OrderAddressesReq) erro
|
||||
return r.SetBody(req).Put("/core/v4/addresses/order")
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) EnableAddress(ctx context.Context, addressID string) error {
|
||||
return c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
|
||||
return r.Put("/core/v4/addresses/" + addressID + "/enable")
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) DisableAddress(ctx context.Context, addressID string) error {
|
||||
return c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
|
||||
return r.Put("/core/v4/addresses/" + addressID + "/disable")
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) DeleteAddress(ctx context.Context, addressID string) error {
|
||||
return c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
|
||||
return r.Delete("/core/v4/addresses/" + addressID)
|
||||
})
|
||||
}
|
||||
|
||||
33
atomic.go
33
atomic.go
@@ -1,33 +0,0 @@
|
||||
package proton
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
type atomicUint64 struct {
|
||||
v uint64
|
||||
}
|
||||
|
||||
func (x *atomicUint64) Load() uint64 { return atomic.LoadUint64(&x.v) }
|
||||
|
||||
func (x *atomicUint64) Store(val uint64) { atomic.StoreUint64(&x.v, val) }
|
||||
|
||||
func (x *atomicUint64) Swap(new uint64) (old uint64) { return atomic.SwapUint64(&x.v, new) }
|
||||
|
||||
func (x *atomicUint64) Add(delta uint64) (new uint64) { return atomic.AddUint64(&x.v, delta) }
|
||||
|
||||
type atomicBool struct {
|
||||
v uint32
|
||||
}
|
||||
|
||||
func (x *atomicBool) Load() bool { return atomic.LoadUint32(&x.v) != 0 }
|
||||
|
||||
func (x *atomicBool) Store(val bool) { atomic.StoreUint32(&x.v, b32(val)) }
|
||||
|
||||
func (x *atomicBool) Swap(new bool) (old bool) { return atomic.SwapUint32(&x.v, b32(new)) != 0 }
|
||||
|
||||
func b32(b bool) uint32 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -16,7 +16,7 @@ func TestAuth(t *testing.T) {
|
||||
s := server.New()
|
||||
defer s.Close()
|
||||
|
||||
_, _, err := s.CreateUser("username", "email@pm.me", []byte("password"))
|
||||
_, _, err := s.CreateUser("user", []byte("password"))
|
||||
require.NoError(t, err)
|
||||
|
||||
m := proton.New(
|
||||
@@ -61,7 +61,7 @@ func TestAuth_Refresh(t *testing.T) {
|
||||
defer s.Close()
|
||||
|
||||
// Create a user on the server.
|
||||
userID, _, err := s.CreateUser("username", "email@pm.me", []byte("password"))
|
||||
userID, _, err := s.CreateUser("user", []byte("password"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// The auth is valid for 4 seconds.
|
||||
@@ -106,7 +106,7 @@ func TestAuth_Refresh_Multi(t *testing.T) {
|
||||
defer s.Close()
|
||||
|
||||
// Create a user on the server.
|
||||
userID, _, err := s.CreateUser("username", "email@pm.me", []byte("password"))
|
||||
userID, _, err := s.CreateUser("user", []byte("password"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// The auth is valid for 4 seconds.
|
||||
@@ -149,7 +149,7 @@ func TestAuth_Refresh_Deauth(t *testing.T) {
|
||||
defer s.Close()
|
||||
|
||||
// Create a user on the server.
|
||||
userID, _, err := s.CreateUser("username", "email@pm.me", []byte("password"))
|
||||
userID, _, err := s.CreateUser("user", []byte("password"))
|
||||
require.NoError(t, err)
|
||||
|
||||
m := proton.New(
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestEventStreamer(t *testing.T) {
|
||||
proton.WithTransport(proton.InsecureTransport()),
|
||||
)
|
||||
|
||||
_, _, err := s.CreateUser("username", "email@pm.me", []byte("password"))
|
||||
_, _, err := s.CreateUser("user", []byte("password"))
|
||||
require.NoError(t, err)
|
||||
|
||||
c, _, err := m.NewClientWithLogin(ctx, "username", []byte("password"))
|
||||
|
||||
5
go.mod
5
go.mod
@@ -8,18 +8,19 @@ require (
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895
|
||||
github.com/ProtonMail/go-srp v0.0.5
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.4.10
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/bradenaw/juniper v0.8.0
|
||||
github.com/emersion/go-message v0.16.0
|
||||
github.com/emersion/go-vcard v0.0.0-20220507122617-d4056df0ec4a
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/urfave/cli/v2 v2.20.3
|
||||
go.uber.org/goleak v1.1.12
|
||||
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
|
||||
google.golang.org/grpc v1.50.1
|
||||
google.golang.org/protobuf v1.28.0
|
||||
)
|
||||
@@ -27,6 +28,7 @@ require (
|
||||
require (
|
||||
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf // indirect
|
||||
github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/cloudflare/circl v1.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cronokirby/saferith v0.33.0 // indirect
|
||||
@@ -50,7 +52,6 @@ require (
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -19,6 +19,10 @@ github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgi
|
||||
github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.4.10 h1:EYgkxzwmQvsa6kxxkgP1AwzkFqKHscF2UINxaSn6rdI=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.4.10/go.mod h1:CTRA7/toc/4DxDy5Du4hPDnIZnJvXSeQ8LsRTOUJoyc=
|
||||
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
|
||||
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
|
||||
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||
github.com/bradenaw/juniper v0.8.0 h1:sdanLNdJbLjcLj993VYIwUHlUVkLzvgiD/x9O7cvvxk=
|
||||
github.com/bradenaw/juniper v0.8.0/go.mod h1:Z2B7aJlQ7xbfWsnMLROj5t/5FQ94/MkIdKC30J4WvzI=
|
||||
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
@@ -79,7 +83,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -174,6 +177,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
|
||||
@@ -41,7 +41,7 @@ func createTestMessages(t *testing.T, c *proton.Client, pass string, count int)
|
||||
Flags: proton.MessageFlagReceived,
|
||||
Unread: true,
|
||||
},
|
||||
Message: []byte(fmt.Sprintf("From: sender@pm.me\r\nReceiver: recipient@pm.me\r\nSubject: %v\r\n\r\nHello World!", uuid.New())),
|
||||
Message: []byte(fmt.Sprintf("From: sender@example.com\r\nReceiver: recipient@example.com\r\nSubject: %v\r\n\r\nHello World!", uuid.New())),
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
20
internal.go
20
internal.go
@@ -1,10 +1,15 @@
|
||||
package proton
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// Quark runs a quark command.
|
||||
func (m *Manager) Quark(ctx context.Context, command string, args ...string) error {
|
||||
if _, err := m.r(ctx).SetQueryParam("strInput", strings.Join(args, " ")).Get("/internal/quark/" + command); err != nil {
|
||||
return err
|
||||
@@ -12,3 +17,18 @@ func (m *Manager) Quark(ctx context.Context, command string, args ...string) err
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// QuarkRes is the same as Quark, but returns the content extracted from the response body.
|
||||
func (m *Manager) QuarkRes(ctx context.Context, command string, args ...string) ([]byte, error) {
|
||||
res, err := m.r(ctx).SetQueryParam("strInput", strings.Join(args, " ")).Get("/internal/quark/" + command)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc, err := html.Parse(bytes.NewReader(res.Body()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []byte(goquery.NewDocumentFromNode(doc).Find(".content").Text()), nil
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ func TestStatus_NoReadExistingConn(t *testing.T) {
|
||||
s := server.New()
|
||||
defer s.Close()
|
||||
|
||||
_, _, err := s.CreateUser("user", "user@pm.me", []byte("pass"))
|
||||
_, _, err := s.CreateUser("user", []byte("pass"))
|
||||
require.NoError(t, err)
|
||||
|
||||
netCtl := proton.NewNetCtl()
|
||||
@@ -197,7 +197,7 @@ func TestStatus_NoWriteExistingConn(t *testing.T) {
|
||||
s := server.New()
|
||||
defer s.Close()
|
||||
|
||||
_, _, err := s.CreateUser("user", "user@pm.me", []byte("pass"))
|
||||
_, _, err := s.CreateUser("user", []byte("pass"))
|
||||
require.NoError(t, err)
|
||||
|
||||
netCtl := proton.NewNetCtl()
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestAuthRefresh(t *testing.T) {
|
||||
s := server.New()
|
||||
defer s.Close()
|
||||
|
||||
_, _, err := s.CreateUser("user", "email@pm.me", []byte("pass"))
|
||||
_, _, err := s.CreateUser("user", []byte("pass"))
|
||||
require.NoError(t, err)
|
||||
|
||||
m := proton.New(
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/ProtonMail/go-proton-api"
|
||||
"github.com/bradenaw/juniper/xslices"
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -25,20 +24,45 @@ func (s *Server) handleGetAddresses() gin.HandlerFunc {
|
||||
|
||||
func (s *Server) handleGetAddress() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
addresses, err := s.b.GetAddresses(c.GetString("UserID"))
|
||||
address, err := s.b.GetAddress(c.GetString("UserID"), c.Param("addressID"))
|
||||
if err != nil {
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"Address": addresses[xslices.IndexFunc(addresses, func(address proton.Address) bool {
|
||||
return address.ID == c.Param("addressID")
|
||||
})],
|
||||
"Address": address,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handlePutAddressEnable() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if err := s.b.EnableAddress(c.GetString("UserID"), c.Param("addressID")); err != nil {
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handlePutAddressDisable() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if err := s.b.DisableAddress(c.GetString("UserID"), c.Param("addressID")); err != nil {
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleDeleteAddress() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if err := s.b.DeleteAddress(c.GetString("UserID"), c.Param("addressID")); err != nil {
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handlePutAddressesOrder() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var req proton.OrderAddressesReq
|
||||
|
||||
@@ -10,6 +10,7 @@ type address struct {
|
||||
addrID string
|
||||
email string
|
||||
order int
|
||||
status proton.AddressStatus
|
||||
keys []key
|
||||
}
|
||||
|
||||
@@ -20,7 +21,7 @@ func (add *address) toAddress() proton.Address {
|
||||
|
||||
Send: true,
|
||||
Receive: true,
|
||||
Status: proton.AddressStatusEnabled,
|
||||
Status: add.status,
|
||||
|
||||
Order: add.order,
|
||||
DisplayName: add.email,
|
||||
|
||||
@@ -50,6 +50,16 @@ func (b *Backend) GetAddressID(email string) (string, error) {
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Backend) GetAddress(userID, addrID string) (proton.Address, error) {
|
||||
return withAcc(b, userID, func(acc *account) (proton.Address, error) {
|
||||
if addr, ok := acc.addresses[addrID]; ok {
|
||||
return addr.toAddress(), nil
|
||||
}
|
||||
|
||||
return proton.Address{}, errors.New("no such address")
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Backend) GetAddresses(userID string) ([]proton.Address, error) {
|
||||
return withAcc(b, userID, func(acc *account) ([]proton.Address, error) {
|
||||
return xslices.Map(maps.Values(acc.addresses), func(add *address) proton.Address {
|
||||
@@ -58,6 +68,55 @@ func (b *Backend) GetAddresses(userID string) ([]proton.Address, error) {
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Backend) EnableAddress(userID, addrID string) error {
|
||||
return b.withAcc(userID, func(acc *account) error {
|
||||
acc.addresses[addrID].status = proton.AddressStatusEnabled
|
||||
|
||||
updateID, err := b.newUpdate(&addressUpdated{addressID: addrID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc.updateIDs = append(acc.updateIDs, updateID)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Backend) DisableAddress(userID, addrID string) error {
|
||||
return b.withAcc(userID, func(acc *account) error {
|
||||
acc.addresses[addrID].status = proton.AddressStatusDisabled
|
||||
|
||||
updateID, err := b.newUpdate(&addressUpdated{addressID: addrID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc.updateIDs = append(acc.updateIDs, updateID)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Backend) DeleteAddress(userID, addrID string) error {
|
||||
return b.withAcc(userID, func(acc *account) error {
|
||||
if acc.addresses[addrID].status != proton.AddressStatusDisabled {
|
||||
return errors.New("address is not disabled")
|
||||
}
|
||||
|
||||
delete(acc.addresses, addrID)
|
||||
|
||||
updateID, err := b.newUpdate(&addressDeleted{addressID: addrID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc.updateIDs = append(acc.updateIDs, updateID)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Backend) SetAddressOrder(userID string, addrIDs []string) error {
|
||||
return b.withAcc(userID, func(acc *account) error {
|
||||
for i, addrID := range addrIDs {
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
)
|
||||
|
||||
type Backend struct {
|
||||
domain string
|
||||
|
||||
accounts map[string]*account
|
||||
accLock sync.RWMutex
|
||||
|
||||
@@ -40,8 +42,9 @@ type Backend struct {
|
||||
authLife time.Duration
|
||||
}
|
||||
|
||||
func New(authLife time.Duration) *Backend {
|
||||
func New(authLife time.Duration, domain string) *Backend {
|
||||
return &Backend{
|
||||
domain: domain,
|
||||
accounts: make(map[string]*account),
|
||||
attachments: make(map[string]*attachment),
|
||||
attData: make(map[string][]byte),
|
||||
@@ -49,8 +52,7 @@ func New(authLife time.Duration) *Backend {
|
||||
labels: make(map[string]*label),
|
||||
updates: make(map[ID]update),
|
||||
srp: make(map[string]*srp.Server),
|
||||
|
||||
authLife: authLife,
|
||||
authLife: authLife,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,31 +192,42 @@ func (b *Backend) RemoveUserKey(userID, keyID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) CreateAddress(userID, email string, password []byte) (string, error) {
|
||||
func (b *Backend) CreateAddress(userID, email string, password []byte, withKey bool) (string, error) {
|
||||
return withAcc(b, userID, func(acc *account) (string, error) {
|
||||
token, err := crypto.RandomToken(32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var keys []key
|
||||
|
||||
armKey, err := GenerateKey(acc.username, email, token, "rsa", 2048)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if withKey {
|
||||
token, err := crypto.RandomToken(32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
passphrase, err := hashPassword([]byte(password), acc.salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
armKey, err := GenerateKey(acc.username, email, token, "rsa", 2048)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
userKR, err := acc.keys[0].unlock(passphrase)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
passphrase, err := hashPassword([]byte(password), acc.salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encToken, sigToken, err := encryptWithSignature(userKR, token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
userKR, err := acc.keys[0].unlock(passphrase)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encToken, sigToken, err := encryptWithSignature(userKR, token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
keys = append(keys, key{
|
||||
keyID: uuid.NewString(),
|
||||
key: armKey,
|
||||
tok: encToken,
|
||||
sig: sigToken,
|
||||
})
|
||||
}
|
||||
|
||||
addressID := uuid.NewString()
|
||||
@@ -223,12 +236,8 @@ func (b *Backend) CreateAddress(userID, email string, password []byte) (string,
|
||||
addrID: addressID,
|
||||
email: email,
|
||||
order: len(acc.addresses) + 1,
|
||||
keys: []key{{
|
||||
keyID: uuid.NewString(),
|
||||
key: armKey,
|
||||
tok: encToken,
|
||||
sig: sigToken,
|
||||
}},
|
||||
status: proton.AddressStatusEnabled,
|
||||
keys: keys,
|
||||
}
|
||||
|
||||
updateID, err := b.newUpdate(&addressCreated{addressID: addressID})
|
||||
|
||||
98
server/backend/quark.go
Normal file
98
server/backend/quark.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/ProtonMail/go-proton-api"
|
||||
)
|
||||
|
||||
func (s *Backend) RunQuarkCommand(command string, args ...string) (any, error) {
|
||||
switch command {
|
||||
case "encryption:id":
|
||||
return s.quarkEncryptionID(args...)
|
||||
|
||||
case "user:create":
|
||||
return s.quarkUserCreate(args...)
|
||||
|
||||
case "user:create:address":
|
||||
return s.quarkUserCreateAddress(args...)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown command: %s", command)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Backend) quarkEncryptionID(args ...string) (string, error) {
|
||||
fs := flag.NewFlagSet("encryption:id", flag.ContinueOnError)
|
||||
|
||||
// Required arguments.
|
||||
// arg0: value
|
||||
|
||||
decrypt := fs.Bool("decrypt", false, "decrypt the given encrypted ID")
|
||||
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// TODO: Encrypt/decrypt are currently no-op.
|
||||
if *decrypt {
|
||||
return fs.Arg(0), nil
|
||||
} else {
|
||||
return fs.Arg(0), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Backend) quarkUserCreate(args ...string) (proton.User, error) {
|
||||
fs := flag.NewFlagSet("user:create", flag.ContinueOnError)
|
||||
|
||||
// Required arguments.
|
||||
name := fs.String("name", "", "new user's name")
|
||||
pass := fs.String("password", "", "new user's password")
|
||||
|
||||
// Optional arguments.
|
||||
newAddr := fs.Bool("create-address", false, "create the user's default address, will not automatically setup the address key")
|
||||
genKeys := fs.String("gen-keys", "", "generate new address keys for the user")
|
||||
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return proton.User{}, err
|
||||
}
|
||||
|
||||
userID, err := s.CreateUser(*name, []byte(*pass))
|
||||
if err != nil {
|
||||
return proton.User{}, fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
|
||||
// TODO: Create keys of different types (we always use RSA2048).
|
||||
if *newAddr || *genKeys != "" {
|
||||
if _, err := s.CreateAddress(userID, *name+"@"+s.domain, []byte(*pass), *genKeys != ""); err != nil {
|
||||
return proton.User{}, fmt.Errorf("failed to create address with keys: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return s.GetUser(userID)
|
||||
}
|
||||
|
||||
func (s *Backend) quarkUserCreateAddress(args ...string) (proton.Address, error) {
|
||||
fs := flag.NewFlagSet("user:create:address", flag.ContinueOnError)
|
||||
|
||||
// Required arguments.
|
||||
// arg0: userID
|
||||
// arg1: password
|
||||
// arg2: email
|
||||
|
||||
// Optional arguments.
|
||||
genKeys := fs.String("gen-keys", "", "generate new address keys for the user")
|
||||
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return proton.Address{}, err
|
||||
}
|
||||
|
||||
// TODO: Create keys of different types (we always use RSA2048).
|
||||
addrID, err := s.CreateAddress(fs.Arg(0), fs.Arg(2), []byte(fs.Arg(1)), *genKeys != "")
|
||||
if err != nil {
|
||||
return proton.Address{}, fmt.Errorf("failed to create address with keys: %w", err)
|
||||
}
|
||||
|
||||
return s.GetAddress(fs.Arg(0), addrID)
|
||||
}
|
||||
@@ -63,11 +63,6 @@ func main() {
|
||||
Usage: "username of the account",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "email",
|
||||
Usage: "email of the account",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "password of the account",
|
||||
@@ -177,7 +172,6 @@ func createUserAction(c *cli.Context) error {
|
||||
|
||||
res, err := client.CreateUser(c.Context, &proto.CreateUserRequest{
|
||||
Username: c.String("username"),
|
||||
Email: c.String("email"),
|
||||
Password: []byte(c.String("password")),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -71,7 +71,7 @@ func (s *service) GetInfo(ctx context.Context, req *proto.GetInfoRequest) (*prot
|
||||
}
|
||||
|
||||
func (s *service) CreateUser(ctx context.Context, req *proto.CreateUserRequest) (*proto.CreateUserResponse, error) {
|
||||
userID, addrID, err := s.server.CreateUser(req.Username, req.Email, req.Password)
|
||||
userID, addrID, err := s.server.CreateUser(req.Username, req.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
11
server/helper_test.go
Normal file
11
server/helper_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func newMessageLiteral(from, to string) []byte {
|
||||
return []byte(fmt.Sprintf("From: %v\r\nReceiver: %v\r\nSubject: %v\r\n\r\nHello World!", from, to, uuid.New()))
|
||||
}
|
||||
22
server/init_test.go
Normal file
22
server/init_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/ProtonMail/go-proton-api/server/backend"
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
)
|
||||
|
||||
func init() {
|
||||
key, err := crypto.GenerateKey("name", "email", "rsa", 1024)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
backend.GenerateKey = func(_, _ string, passphrase []byte, _ string, _ int) (string, error) {
|
||||
encKey, err := key.Lock(passphrase)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return encKey.Armor()
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.7
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc v3.21.10
|
||||
// source: server.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -166,7 +165,6 @@ type CreateUserRequest struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
|
||||
Password []byte `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
|
||||
}
|
||||
|
||||
@@ -209,13 +207,6 @@ func (x *CreateUserRequest) GetUsername() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateUserRequest) GetEmail() string {
|
||||
if x != nil {
|
||||
return x.Email
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateUserRequest) GetPassword() []byte {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
@@ -694,80 +685,79 @@ var file_server_proto_rawDesc = []byte{
|
||||
0x73, 0x74, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73,
|
||||
0x74, 0x55, 0x52, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c,
|
||||
0x22, 0x61, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
|
||||
0x22, 0x4b, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x22, 0x44, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65,
|
||||
0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65,
|
||||
0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49,
|
||||
0x44, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x64, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x06, 0x61, 0x64, 0x64, 0x72, 0x49, 0x44, 0x22, 0x2b, 0x0a, 0x11, 0x52, 0x65, 0x76,
|
||||
0x6f, 0x6b, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16,
|
||||
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x44, 0x0a,
|
||||
0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x61,
|
||||
0x64, 0x64, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x64, 0x64,
|
||||
0x72, 0x49, 0x44, 0x22, 0x2b, 0x0a, 0x11, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x55, 0x73, 0x65,
|
||||
0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72,
|
||||
0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44,
|
||||
0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x60, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||
0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65,
|
||||
0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x60, 0x0a, 0x14,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61,
|
||||
0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x2f,
|
||||
0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x64, 0x72, 0x49,
|
||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x64, 0x64, 0x72, 0x49, 0x44, 0x22,
|
||||
0x46, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49,
|
||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x61, 0x64, 0x64, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x61, 0x64, 0x64, 0x72, 0x49, 0x44, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x76,
|
||||
0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x82, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49,
|
||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12,
|
||||
0x24, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c,
|
||||
0x61, 0x62, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x6c, 0x61, 0x62, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c,
|
||||
0x61, 0x62, 0x65, 0x6c, 0x49, 0x44, 0x2a, 0x22, 0x0a, 0x09, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54,
|
||||
0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x10, 0x00, 0x12,
|
||||
0x09, 0x0a, 0x05, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x10, 0x01, 0x32, 0xa6, 0x03, 0x0a, 0x06, 0x53,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x12, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
|
||||
0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x41, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x18, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x55, 0x73, 0x65, 0x72,
|
||||
0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x55,
|
||||
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61,
|
||||
0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x2f, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
|
||||
0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a,
|
||||
0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x6e, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x68, 0x2f, 0x67, 0x6f, 0x2f, 0x6c,
|
||||
0x69, 0x74, 0x65, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x64, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x06, 0x61, 0x64, 0x64, 0x72, 0x49, 0x44, 0x22, 0x46, 0x0a, 0x14, 0x52, 0x65, 0x6d,
|
||||
0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x64,
|
||||
0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x64, 0x64, 0x72, 0x49,
|
||||
0x44, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x12, 0x43,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x04, 0x74, 0x79, 0x70,
|
||||
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
|
||||
0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22,
|
||||
0x2f, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x49,
|
||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x49, 0x44,
|
||||
0x2a, 0x22, 0x0a, 0x09, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a,
|
||||
0x06, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x41, 0x42,
|
||||
0x45, 0x4c, 0x10, 0x01, 0x32, 0xa6, 0x03, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12,
|
||||
0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x43, 0x72, 0x65,
|
||||
0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a,
|
||||
0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x76,
|
||||
0x6f, 0x6b, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x4a, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x52,
|
||||
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x32, 0x5a,
|
||||
0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x6e, 0x4d, 0x61, 0x69, 0x6c, 0x2f, 0x67, 0x6f, 0x2d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e,
|
||||
0x2d, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -33,7 +33,6 @@ message GetInfoResponse {
|
||||
|
||||
message CreateUserRequest {
|
||||
string username = 1;
|
||||
string email = 2;
|
||||
bytes password = 3;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.7
|
||||
// - protoc v3.21.10
|
||||
// source: server.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
|
||||
51
server/quark.go
Normal file
51
server/quark.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// TODO: This is a disgusting hack to match the output of the internal quark command.
|
||||
// They should return JSON instead of HTML!
|
||||
func (s *Server) handleQuarkCommand() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
res, err := s.b.RunQuarkCommand(c.Param("command"), strings.Split(c.Query("strInput"), " ")...)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
var out string
|
||||
|
||||
switch res := res.(type) {
|
||||
case string:
|
||||
out = res
|
||||
|
||||
default:
|
||||
b, err := json.MarshalIndent(res, "", " ")
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
out = string(b)
|
||||
}
|
||||
|
||||
tmp, err := template.New("quarkCommand").Parse(`<html><body><div class="content">{{.Content}}</div></body></html>`)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tmp.Execute(c.Writer, map[string]string{
|
||||
"Content": template.HTMLEscapeString(out),
|
||||
}); err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
73
server/quark_test.go
Normal file
73
server/quark_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/ProtonMail/go-proton-api"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestServer_Quark_CreateUser(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, _ *Server, m *proton.Manager) {
|
||||
// Create two users, one with keys and one without.
|
||||
require.NoError(t, m.Quark(ctx, "user:create", "--name", "user-no-keys", "--password", "test", "--create-address"))
|
||||
require.NoError(t, m.Quark(ctx, "user:create", "--name", "user-keys", "--password", "test", "--gen-keys", "rsa2048"))
|
||||
|
||||
{
|
||||
// The address should be created but should have no keys.
|
||||
c, _, err := m.NewClientWithLogin(ctx, "user-no-keys", []byte("test"))
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
addr, err := c.GetAddresses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, addr, 1)
|
||||
require.Len(t, addr[0].Keys, 0)
|
||||
}
|
||||
|
||||
{
|
||||
// The address should be created and should have keys.
|
||||
c, _, err := m.NewClientWithLogin(ctx, "user-keys", []byte("test"))
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
addr, err := c.GetAddresses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, addr, 1)
|
||||
require.Len(t, addr[0].Keys, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Quark_CreateAddress(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, _ *Server, m *proton.Manager) {
|
||||
// Create a user with one address.
|
||||
require.NoError(t, m.Quark(ctx, "user:create", "--name", "user", "--password", "test", "--gen-keys", "rsa2048"))
|
||||
|
||||
// Login to the user.
|
||||
c, _, err := m.NewClientWithLogin(ctx, "user", []byte("test"))
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
// Get the user.
|
||||
user, err := c.GetUser(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Initially the user should have one address and it should have keys.
|
||||
addr, err := c.GetAddresses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, addr, 1)
|
||||
require.Len(t, addr[0].Keys, 1)
|
||||
|
||||
// Create a new address.
|
||||
require.NoError(t, m.Quark(ctx, "user:create:address", "--gen-keys", "rsa2048", user.ID, "test", "alias@proton.local"))
|
||||
|
||||
// Now the user should have two addresses, and they should both have keys.
|
||||
newAddr, err := c.GetAddresses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, newAddr, 2)
|
||||
require.Len(t, newAddr[0].Keys, 1)
|
||||
require.Len(t, newAddr[1].Keys, 1)
|
||||
})
|
||||
}
|
||||
@@ -48,6 +48,9 @@ func initRouter(s *Server) {
|
||||
if addresses := core.Group("/addresses"); addresses != nil {
|
||||
addresses.GET("", s.handleGetAddresses())
|
||||
addresses.GET("/:addressID", s.handleGetAddress())
|
||||
addresses.DELETE("/:addressID", s.handleDeleteAddress())
|
||||
addresses.PUT("/:addressID/enable", s.handlePutAddressEnable())
|
||||
addresses.PUT("/:addressID/disable", s.handlePutAddressDisable())
|
||||
addresses.PUT("/order", s.handlePutAddressesOrder())
|
||||
}
|
||||
|
||||
@@ -114,6 +117,11 @@ func initRouter(s *Server) {
|
||||
tests.GET("/ping", s.handleGetPing())
|
||||
}
|
||||
|
||||
// Quark routes don't need authentication.
|
||||
if quark := s.r.Group("/internal/quark"); quark != nil {
|
||||
quark.GET("/:command", s.handleQuarkCommand())
|
||||
}
|
||||
|
||||
// Proxy any calls to the upstream server.
|
||||
if proxy := s.r.Group("/proxy"); proxy != nil {
|
||||
proxy.Any("/*path", s.handleProxy(proxy.BasePath()))
|
||||
|
||||
@@ -33,7 +33,10 @@ type Server struct {
|
||||
callWatchers []callWatcher
|
||||
callWatchersLock sync.RWMutex
|
||||
|
||||
// MinAppVersion is the minimum app version that the server will accept.
|
||||
// domain is the test server domain.
|
||||
domain string
|
||||
|
||||
// minAppVersion is the minimum app version that the server will accept.
|
||||
minAppVersion *semver.Version
|
||||
|
||||
// proxyOrigin is the URL of the origin server when the server is a proxy.
|
||||
@@ -56,6 +59,7 @@ func New(opts ...Option) *Server {
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
// GetHostURL returns the API root to make calls to.
|
||||
func (s *Server) GetHostURL() string {
|
||||
return s.s.URL
|
||||
}
|
||||
@@ -65,6 +69,11 @@ func (s *Server) GetProxyURL() string {
|
||||
return s.s.URL + "/proxy"
|
||||
}
|
||||
|
||||
// GetDomain returns the domain of the server.
|
||||
func (s *Server) GetDomain() string {
|
||||
return s.domain
|
||||
}
|
||||
|
||||
func (s *Server) AddCallWatcher(fn func(Call), paths ...string) {
|
||||
s.callWatchersLock.Lock()
|
||||
defer s.callWatchersLock.Unlock()
|
||||
@@ -72,13 +81,15 @@ func (s *Server) AddCallWatcher(fn func(Call), paths ...string) {
|
||||
s.callWatchers = append(s.callWatchers, newCallWatcher(fn, paths...))
|
||||
}
|
||||
|
||||
func (s *Server) CreateUser(username, email string, password []byte) (string, string, error) {
|
||||
// CreateUser creates a new server user with the given username and password.
|
||||
// A single address will be created for the user, derived from the username and the server's domain.
|
||||
func (s *Server) CreateUser(username string, password []byte) (string, string, error) {
|
||||
userID, err := s.b.CreateUser(username, password)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
addrID, err := s.b.CreateAddress(userID, email, password)
|
||||
addrID, err := s.b.CreateAddress(userID, username+"@"+s.domain, password, true)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@@ -114,7 +125,7 @@ func (s *Server) RemoveUserKey(userID, keyID string) error {
|
||||
}
|
||||
|
||||
func (s *Server) CreateAddress(userID, email string, password []byte) (string, error) {
|
||||
return s.b.CreateAddress(userID, email, password)
|
||||
return s.b.CreateAddress(userID, email, password, true)
|
||||
}
|
||||
|
||||
func (s *Server) RemoveAddress(userID, addrID string) error {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
type serverBuilder struct {
|
||||
withTLS bool
|
||||
domain string
|
||||
logger io.Writer
|
||||
origin string
|
||||
cacher AuthCacher
|
||||
@@ -29,6 +30,7 @@ func newServerBuilder() *serverBuilder {
|
||||
|
||||
return &serverBuilder{
|
||||
withTLS: true,
|
||||
domain: "proton.local",
|
||||
logger: logger,
|
||||
origin: proton.DefaultHostURL,
|
||||
}
|
||||
@@ -39,8 +41,9 @@ func (builder *serverBuilder) build() *Server {
|
||||
|
||||
s := &Server{
|
||||
r: gin.New(),
|
||||
b: backend.New(time.Hour),
|
||||
b: backend.New(time.Hour, builder.domain),
|
||||
|
||||
domain: builder.domain,
|
||||
proxyOrigin: builder.origin,
|
||||
authCacher: builder.cacher,
|
||||
}
|
||||
@@ -83,6 +86,21 @@ func (opt withTLS) config(builder *serverBuilder) {
|
||||
builder.withTLS = opt.withTLS
|
||||
}
|
||||
|
||||
// withDomain controls the domain of the server.
|
||||
func WithDomain(domain string) Option {
|
||||
return &withDomain{
|
||||
domain: domain,
|
||||
}
|
||||
}
|
||||
|
||||
type withDomain struct {
|
||||
domain string
|
||||
}
|
||||
|
||||
func (opt withDomain) config(builder *serverBuilder) {
|
||||
builder.domain = opt.domain
|
||||
}
|
||||
|
||||
// WithLogger controls where Gin logs to.
|
||||
func WithLogger(logger io.Writer) Option {
|
||||
return &withLogger{
|
||||
|
||||
@@ -26,13 +26,13 @@ import (
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
func TestServer_LoginLogout(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
user, err := c.GetUser(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "user", user.Name)
|
||||
require.Equal(t, "email@pm.me", user.Email)
|
||||
require.Equal(t, "user@"+s.GetDomain(), user.Email)
|
||||
|
||||
// Logout from the test API.
|
||||
require.NoError(t, c.AuthDelete(ctx))
|
||||
@@ -45,7 +45,7 @@ func TestServer(t *testing.T) {
|
||||
|
||||
func TestServerMulti(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
_, _, err := s.CreateUser("user", "email@pm.me", []byte("pass"))
|
||||
_, _, err := s.CreateUser("user", []byte("pass"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create one client.
|
||||
@@ -120,7 +120,7 @@ func TestServer_Ping(t *testing.T) {
|
||||
|
||||
func TestServer_Bool(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 1, func([]string) {
|
||||
metadata, err := c.GetMessageMetadata(ctx, proton.MessageFilter{})
|
||||
require.NoError(t, err)
|
||||
@@ -140,7 +140,7 @@ func TestServer_Bool(t *testing.T) {
|
||||
|
||||
func TestServer_Messages(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 1000, func(messageIDs []string) {
|
||||
// Get the messages.
|
||||
metadata, err := c.GetMessageMetadata(ctx, proton.MessageFilter{})
|
||||
@@ -180,7 +180,7 @@ func TestServer_Messages(t *testing.T) {
|
||||
|
||||
func TestServer_MessageFilter(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 1000, func(messageIDs []string) {
|
||||
// Get the messages.
|
||||
metadata, err := c.GetMessageMetadata(ctx, proton.MessageFilter{})
|
||||
@@ -210,7 +210,7 @@ func TestServer_MessageFilter(t *testing.T) {
|
||||
|
||||
func TestServer_MessageIDs(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 10000, func(wantMessageIDs []string) {
|
||||
allMessageIDs, err := c.GetMessageIDs(ctx, "")
|
||||
require.NoError(t, err)
|
||||
@@ -226,7 +226,7 @@ func TestServer_MessageIDs(t *testing.T) {
|
||||
|
||||
func TestServer_MessagesDelete(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 1000, func(messageIDs []string) {
|
||||
// Get the messages.
|
||||
metadata, err := c.GetMessageMetadata(ctx, proton.MessageFilter{})
|
||||
@@ -255,7 +255,7 @@ func TestServer_MessagesDelete(t *testing.T) {
|
||||
|
||||
func TestServer_MessagesDeleteAfterUpdate(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 1000, func(messageIDs []string) {
|
||||
// Get the initial event ID.
|
||||
eventID, err := c.GetLatestEventID(ctx)
|
||||
@@ -285,7 +285,7 @@ func TestServer_MessagesDeleteAfterUpdate(t *testing.T) {
|
||||
|
||||
func TestServer_Events(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 3, func(messageIDs []string) {
|
||||
// Get the latest event ID to stream from.
|
||||
fromEventID, err := c.GetLatestEventID(ctx)
|
||||
@@ -367,7 +367,7 @@ func TestServer_Events(t *testing.T) {
|
||||
func TestServer_Events_Multi(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
for i := 0; i < 10; i++ {
|
||||
withUser(ctx, t, s, m, fmt.Sprintf("user%v", i), fmt.Sprintf("email%v@pm.me", i), "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, fmt.Sprintf("user%v", i), "pass", func(c *proton.Client) {
|
||||
latest, err := c.GetLatestEventID(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -393,7 +393,7 @@ func TestServer_Events_Multi(t *testing.T) {
|
||||
|
||||
func TestServer_Events_Refresh(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
user, err := c.GetUser(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -417,11 +417,11 @@ func TestServer_Events_Refresh(t *testing.T) {
|
||||
|
||||
func TestServer_RevokeUser(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
user, err := c.GetUser(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "user", user.Name)
|
||||
require.Equal(t, "email@pm.me", user.Email)
|
||||
require.Equal(t, "user@"+s.GetDomain(), user.Email)
|
||||
|
||||
// Revoke the user's auth.
|
||||
require.NoError(t, s.RevokeUser(user.ID))
|
||||
@@ -434,7 +434,7 @@ func TestServer_RevokeUser(t *testing.T) {
|
||||
|
||||
func TestServer_Calls(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
var calls []Call
|
||||
|
||||
// Watch calls that are made.
|
||||
@@ -466,7 +466,7 @@ func TestServer_Calls(t *testing.T) {
|
||||
|
||||
func TestServer_Calls_Status(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
var calls []Call
|
||||
|
||||
// Watch calls that are made.
|
||||
@@ -492,7 +492,7 @@ func TestServer_Calls_Request(t *testing.T) {
|
||||
calls = append(calls, call)
|
||||
})
|
||||
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(*proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(*proton.Client) {
|
||||
require.Equal(
|
||||
t,
|
||||
calls[0].RequestBody,
|
||||
@@ -510,7 +510,7 @@ func TestServer_Calls_Response(t *testing.T) {
|
||||
calls = append(calls, call)
|
||||
})
|
||||
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
salts, err := c.GetSalts(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -531,7 +531,7 @@ func TestServer_Calls_Cookies(t *testing.T) {
|
||||
calls = append(calls, call)
|
||||
})
|
||||
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(*proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(*proton.Client) {
|
||||
// The header in the first call's response should set the Session-Id cookie.
|
||||
resHeader := (&http.Response{Header: calls[len(calls)-2].ResponseHeader})
|
||||
require.Len(t, resHeader.Cookies(), 1)
|
||||
@@ -572,7 +572,7 @@ func TestServer_Calls_Manager(t *testing.T) {
|
||||
|
||||
func TestServer_CreateMessage(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
user, err := c.GetUser(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -592,14 +592,15 @@ func TestServer_CreateMessage(t *testing.T) {
|
||||
Message: proton.DraftTemplate{
|
||||
Subject: "My subject",
|
||||
Sender: &mail.Address{Address: addr[0].Email},
|
||||
ToList: []*mail.Address{{Address: "recipient@pm.me"}},
|
||||
ToList: []*mail.Address{{Address: "recipient@example.com"}},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, addr[0].ID, draft.AddressID)
|
||||
require.Equal(t, "My subject", draft.Subject)
|
||||
require.Equal(t, &mail.Address{Address: "email@pm.me"}, draft.Sender)
|
||||
require.Equal(t, &mail.Address{Address: "user@" + s.GetDomain()}, draft.Sender)
|
||||
require.Equal(t, []*mail.Address{{Address: "recipient@example.com"}}, draft.ToList)
|
||||
require.ElementsMatch(t, []string{proton.AllMailLabel, proton.AllDraftsLabel, proton.DraftsLabel}, draft.LabelIDs)
|
||||
})
|
||||
})
|
||||
@@ -607,7 +608,7 @@ func TestServer_CreateMessage(t *testing.T) {
|
||||
|
||||
func TestServer_UpdateDraft(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
user, err := c.GetUser(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -628,13 +629,14 @@ func TestServer_UpdateDraft(t *testing.T) {
|
||||
Message: proton.DraftTemplate{
|
||||
Subject: "My subject",
|
||||
Sender: &mail.Address{Address: addr[0].Email},
|
||||
ToList: []*mail.Address{{Address: "recipient@pm.me"}},
|
||||
ToList: []*mail.Address{{Address: "recipient@example.com"}},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addr[0].ID, draft.AddressID)
|
||||
require.Equal(t, "My subject", draft.Subject)
|
||||
require.Equal(t, &mail.Address{Address: "email@pm.me"}, draft.Sender)
|
||||
require.Equal(t, &mail.Address{Address: "user@" + s.GetDomain()}, draft.Sender)
|
||||
require.Equal(t, []*mail.Address{{Address: "recipient@example.com"}}, draft.ToList)
|
||||
|
||||
// Create an event stream to watch for an update event.
|
||||
fromEventID, err := c.GetLatestEventID(ctx)
|
||||
@@ -646,7 +648,7 @@ func TestServer_UpdateDraft(t *testing.T) {
|
||||
msg, err := c.UpdateDraft(ctx, draft.ID, addrKRs[addr[0].ID], proton.UpdateDraftReq{
|
||||
Message: proton.DraftTemplate{
|
||||
Subject: "Edited subject",
|
||||
ToList: []*mail.Address{{Address: "edited@pm.me"}},
|
||||
ToList: []*mail.Address{{Address: "edited@example.com"}},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -670,7 +672,7 @@ func TestServer_UpdateDraft(t *testing.T) {
|
||||
|
||||
require.Equal(t, draft.ID, event.Messages[0].ID)
|
||||
require.Equal(t, "Edited subject", event.Messages[0].Message.Subject)
|
||||
require.Equal(t, []*mail.Address{{Address: "edited@pm.me"}}, event.Messages[0].Message.ToList)
|
||||
require.Equal(t, []*mail.Address{{Address: "edited@example.com"}}, event.Messages[0].Message.ToList)
|
||||
|
||||
return true
|
||||
}, 5*time.Second, time.Millisecond*100)
|
||||
@@ -680,7 +682,7 @@ func TestServer_UpdateDraft(t *testing.T) {
|
||||
|
||||
func TestServer_SendMessage(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
user, err := c.GetUser(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -700,7 +702,7 @@ func TestServer_SendMessage(t *testing.T) {
|
||||
Message: proton.DraftTemplate{
|
||||
Subject: "My subject",
|
||||
Sender: &mail.Address{Address: addr[0].Email},
|
||||
ToList: []*mail.Address{{Address: "recipient@pm.me"}},
|
||||
ToList: []*mail.Address{{Address: "recipient@example.com"}},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -711,6 +713,7 @@ func TestServer_SendMessage(t *testing.T) {
|
||||
require.Equal(t, draft.ID, sent.ID)
|
||||
require.Equal(t, addr[0].ID, sent.AddressID)
|
||||
require.Equal(t, "My subject", sent.Subject)
|
||||
require.Equal(t, []*mail.Address{{Address: "recipient@example.com"}}, sent.ToList)
|
||||
require.Contains(t, sent.LabelIDs, proton.SentLabel)
|
||||
})
|
||||
})
|
||||
@@ -718,7 +721,7 @@ func TestServer_SendMessage(t *testing.T) {
|
||||
|
||||
func TestServer_AuthDelete(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
require.NoError(t, c.AuthDelete(ctx))
|
||||
})
|
||||
})
|
||||
@@ -733,7 +736,7 @@ func TestServer_ForceUpgrade(t *testing.T) {
|
||||
|
||||
s.SetMinAppVersion(semver.MustParse("1.0.0"))
|
||||
|
||||
if _, _, err := s.CreateUser("user", "email@pm.me", []byte("pass")); err != nil {
|
||||
if _, _, err := s.CreateUser("user", []byte("pass")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -759,7 +762,7 @@ func TestServer_ForceUpgrade(t *testing.T) {
|
||||
|
||||
func TestServer_Import(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@@ -913,7 +916,7 @@ func TestServer_Labels(t *testing.T) {
|
||||
}
|
||||
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@@ -1055,7 +1058,7 @@ func TestServer_Import_FlagsAndLabels(t *testing.T) {
|
||||
}
|
||||
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@@ -1082,7 +1085,7 @@ func TestServer_Import_FlagsAndLabels(t *testing.T) {
|
||||
Flags: tt.flags,
|
||||
LabelIDs: tt.labelIDs,
|
||||
},
|
||||
Message: []byte(fmt.Sprintf("From: sender@pm.me\r\nReceiver: recipient@pm.me\r\nSubject: %v\r\n\r\nHello World!", uuid.New())),
|
||||
Message: newMessageLiteral("sender@example.com", "recipient@example.com"),
|
||||
}}...))
|
||||
if tt.wantError {
|
||||
require.Error(t, err)
|
||||
@@ -1107,12 +1110,12 @@ func TestServer_Import_FlagsAndLabels(t *testing.T) {
|
||||
|
||||
func TestServer_PublicKeys(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
if _, _, err := s.CreateUser("other", "other@pm.me", []byte("pass")); err != nil {
|
||||
if _, _, err := s.CreateUser("other", []byte("pass")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
intKeys, intType, err := c.GetPublicKeys(ctx, "other@pm.me")
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
intKeys, intType, err := c.GetPublicKeys(ctx, "other@"+s.GetDomain())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, proton.RecipientTypeInternal, intType)
|
||||
require.Len(t, intKeys, 1)
|
||||
@@ -1133,8 +1136,11 @@ func TestServer_Proxy(t *testing.T) {
|
||||
calls = append(calls, call)
|
||||
})
|
||||
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(_ *proton.Client) {
|
||||
proxy := New(WithProxyOrigin(s.GetHostURL()))
|
||||
withUser(ctx, t, s, m, "user", "pass", func(_ *proton.Client) {
|
||||
proxy := New(
|
||||
WithProxyOrigin(s.GetHostURL()),
|
||||
WithProxyTransport(proton.InsecureTransport()),
|
||||
)
|
||||
defer proxy.Close()
|
||||
|
||||
m := proton.New(
|
||||
@@ -1161,9 +1167,10 @@ func TestServer_Proxy(t *testing.T) {
|
||||
|
||||
func TestServer_Proxy_Cache(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(_ *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(_ *proton.Client) {
|
||||
proxy := New(
|
||||
WithProxyOrigin(s.GetHostURL()),
|
||||
WithProxyTransport(proton.InsecureTransport()),
|
||||
WithAuthCacher(NewAuthCache()),
|
||||
)
|
||||
defer proxy.Close()
|
||||
@@ -1190,9 +1197,10 @@ func TestServer_Proxy_Cache(t *testing.T) {
|
||||
|
||||
func TestServer_Proxy_AuthDelete(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(_ *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(_ *proton.Client) {
|
||||
proxy := New(
|
||||
WithProxyOrigin(s.GetHostURL()),
|
||||
WithProxyTransport(proton.InsecureTransport()),
|
||||
WithAuthCacher(NewAuthCache()),
|
||||
)
|
||||
defer proxy.Close()
|
||||
@@ -1299,7 +1307,7 @@ func TestServer_RealProxy_Cache(t *testing.T) {
|
||||
|
||||
func TestServer_Messages_Fetch(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 1000, func(messageIDs []string) {
|
||||
ctl := proton.NewNetCtl()
|
||||
|
||||
@@ -1341,7 +1349,7 @@ func TestServer_Messages_Fetch(t *testing.T) {
|
||||
|
||||
func TestServer_Messages_Status(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
withMessages(ctx, t, c, "pass", 1000, func(messageIDs []string) {
|
||||
ctl := proton.NewNetCtl()
|
||||
|
||||
@@ -1381,7 +1389,7 @@ func TestServer_Messages_Status(t *testing.T) {
|
||||
|
||||
func TestServer_Labels_Duplicates(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
req := proton.CreateLabelReq{
|
||||
Name: uuid.NewString(),
|
||||
Color: "#f66",
|
||||
@@ -1400,7 +1408,7 @@ func TestServer_Labels_Duplicates(t *testing.T) {
|
||||
|
||||
func TestServer_Labels_Duplicates_Update(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
label1, err := c.CreateLabel(context.Background(), proton.CreateLabelReq{
|
||||
Name: uuid.NewString(),
|
||||
Color: "#f66",
|
||||
@@ -1441,7 +1449,7 @@ func TestServer_Labels_Duplicates_Update(t *testing.T) {
|
||||
|
||||
func TestServer_Labels_Subfolders(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
parent, err := c.CreateLabel(context.Background(), proton.CreateLabelReq{
|
||||
Name: uuid.NewString(),
|
||||
Color: "#f66",
|
||||
@@ -1472,7 +1480,7 @@ func TestServer_Labels_Subfolders(t *testing.T) {
|
||||
|
||||
func TestServer_Labels_Subfolders_Reassign(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
parent1, err := c.CreateLabel(context.Background(), proton.CreateLabelReq{
|
||||
Name: uuid.NewString(),
|
||||
Color: "#f66",
|
||||
@@ -1520,7 +1528,7 @@ func TestServer_Labels_Subfolders_Reassign(t *testing.T) {
|
||||
|
||||
func TestServer_Labels_Subfolders_DeleteParentWithChildren(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
parent, err := c.CreateLabel(context.Background(), proton.CreateLabelReq{
|
||||
Name: uuid.NewString(),
|
||||
Color: "#f66",
|
||||
@@ -1564,9 +1572,54 @@ func TestServer_Labels_Subfolders_DeleteParentWithChildren(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_AddressCreateDelete(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
user, err := c.GetUser(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an address.
|
||||
alias, err := s.CreateAddress(user.ID, "alias@example.com", []byte("pass"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// The user should have two addresses, both enabled.
|
||||
{
|
||||
addr, err := c.GetAddresses(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, addr, 2)
|
||||
require.Equal(t, addr[0].Status, proton.AddressStatusEnabled)
|
||||
require.Equal(t, addr[1].Status, proton.AddressStatusEnabled)
|
||||
}
|
||||
|
||||
// Disable the alias.
|
||||
require.NoError(t, c.DisableAddress(context.Background(), alias))
|
||||
|
||||
// The user should have two addresses, the primary enabled and the alias disabled.
|
||||
{
|
||||
addr, err := c.GetAddresses(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, addr, 2)
|
||||
require.Equal(t, addr[0].Status, proton.AddressStatusEnabled)
|
||||
require.Equal(t, addr[1].Status, proton.AddressStatusDisabled)
|
||||
}
|
||||
|
||||
// Delete the alias.
|
||||
require.NoError(t, c.DeleteAddress(context.Background(), alias))
|
||||
|
||||
// The user should have one address, the primary enabled.
|
||||
{
|
||||
addr, err := c.GetAddresses(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, addr, 1)
|
||||
require.Equal(t, addr[0].Status, proton.AddressStatusEnabled)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_AddressOrder(t *testing.T) {
|
||||
withServer(t, func(ctx context.Context, s *Server, m *proton.Manager) {
|
||||
withUser(ctx, t, s, m, "user", "email@pm.me", "pass", func(c *proton.Client) {
|
||||
withUser(ctx, t, s, m, "user", "pass", func(c *proton.Client) {
|
||||
user, err := c.GetUser(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1574,13 +1627,13 @@ func TestServer_AddressOrder(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create 3 additional addresses.
|
||||
addr1, err := s.CreateAddress(user.ID, "addr1@pm.me", []byte("pass"))
|
||||
addr1, err := s.CreateAddress(user.ID, "addr1@example.com", []byte("pass"))
|
||||
require.NoError(t, err)
|
||||
|
||||
addr2, err := s.CreateAddress(user.ID, "addr2@pm.me", []byte("pass"))
|
||||
addr2, err := s.CreateAddress(user.ID, "addr2@example.com", []byte("pass"))
|
||||
require.NoError(t, err)
|
||||
|
||||
addr3, err := s.CreateAddress(user.ID, "addr3@pm.me", []byte("pass"))
|
||||
addr3, err := s.CreateAddress(user.ID, "addr3@example.com", []byte("pass"))
|
||||
require.NoError(t, err)
|
||||
|
||||
addresses, err := c.GetAddresses(context.Background())
|
||||
@@ -1626,11 +1679,11 @@ func withServer(t *testing.T, fn func(ctx context.Context, s *Server, m *proton.
|
||||
fn(ctx, s, m)
|
||||
}
|
||||
|
||||
func withUser(ctx context.Context, t *testing.T, s *Server, m *proton.Manager, username, email, password string, fn func(c *proton.Client)) {
|
||||
_, _, err := s.CreateUser(username, email, []byte(password))
|
||||
func withUser(ctx context.Context, t *testing.T, s *Server, m *proton.Manager, user, pass string, fn func(c *proton.Client)) {
|
||||
_, _, err := s.CreateUser(user, []byte(pass))
|
||||
require.NoError(t, err)
|
||||
|
||||
c, _, err := m.NewClientWithLogin(ctx, username, []byte(password))
|
||||
c, _, err := m.NewClientWithLogin(ctx, user, []byte(pass))
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
@@ -1676,7 +1729,7 @@ func importMessages(
|
||||
Flags: flags,
|
||||
Unread: true,
|
||||
},
|
||||
Message: []byte(fmt.Sprintf("From: sender@pm.me\r\nReceiver: recipient@pm.me\r\nSubject: %v\r\n\r\nHello World!", uuid.New())),
|
||||
Message: newMessageLiteral("sender@example.com", "recipient@example.com"),
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
6
server/testdata/text-plain.eml
vendored
6
server/testdata/text-plain.eml
vendored
@@ -1,6 +0,0 @@
|
||||
To: recipient@pm.me
|
||||
From: sender@pm.me
|
||||
Subject: Test
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
Test
|
||||
Reference in New Issue
Block a user