forked from Silverfish/proton-bridge
GODT-1815: Combined/Split mode
This commit is contained in:
@ -5,9 +5,14 @@ import (
|
||||
)
|
||||
|
||||
// RandomToken is a function that returns a random token.
|
||||
var RandomToken func(size int) ([]byte, error)
|
||||
|
||||
// By default, we use crypto.RandomToken to generate tokens.
|
||||
func init() {
|
||||
RandomToken = crypto.RandomToken
|
||||
var RandomToken = crypto.RandomToken
|
||||
|
||||
func newRandomToken(size int) []byte {
|
||||
token, err := RandomToken(size)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/ProtonMail/gluon/imap"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||
)
|
||||
|
||||
@ -45,15 +46,24 @@ type Settings struct {
|
||||
FirstStartGUI bool
|
||||
}
|
||||
|
||||
type AddressMode int
|
||||
|
||||
const (
|
||||
CombinedMode AddressMode = iota
|
||||
SplitMode
|
||||
)
|
||||
|
||||
// UserData holds information about a single bridge user.
|
||||
// The user may or may not be logged in.
|
||||
type UserData struct {
|
||||
UserID string
|
||||
Username string
|
||||
|
||||
GluonID string
|
||||
GluonKey []byte
|
||||
BridgePass string
|
||||
GluonKey []byte
|
||||
GluonIDs map[string]string
|
||||
UIDValidity map[string]imap.UID
|
||||
BridgePass []byte
|
||||
AddressMode AddressMode
|
||||
|
||||
AuthUID string
|
||||
AuthRef string
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/ProtonMail/gluon/imap"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
vault *Vault
|
||||
userID string
|
||||
@ -13,16 +19,41 @@ func (user *User) Username() string {
|
||||
return user.vault.getUser(user.userID).Username
|
||||
}
|
||||
|
||||
func (user *User) GluonID() string {
|
||||
return user.vault.getUser(user.userID).GluonID
|
||||
func (user *User) GetGluonIDs() map[string]string {
|
||||
return user.vault.getUser(user.userID).GluonIDs
|
||||
}
|
||||
|
||||
func (user *User) SetGluonID(addrID, gluonID string) error {
|
||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||
data.GluonIDs[addrID] = gluonID
|
||||
})
|
||||
}
|
||||
|
||||
func (user *User) GetUIDValidity(addrID string) (imap.UID, bool) {
|
||||
validity, ok := user.vault.getUser(user.userID).UIDValidity[addrID]
|
||||
if !ok {
|
||||
return imap.UID(0), false
|
||||
}
|
||||
|
||||
return validity, true
|
||||
}
|
||||
|
||||
func (user *User) SetUIDValidity(addrID string, validity imap.UID) error {
|
||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||
data.UIDValidity[addrID] = validity
|
||||
})
|
||||
}
|
||||
|
||||
func (user *User) GluonKey() []byte {
|
||||
return user.vault.getUser(user.userID).GluonKey
|
||||
}
|
||||
|
||||
func (user *User) AddressMode() AddressMode {
|
||||
return user.vault.getUser(user.userID).AddressMode
|
||||
}
|
||||
|
||||
func (user *User) BridgePass() string {
|
||||
return user.vault.getUser(user.userID).BridgePass
|
||||
return hex.EncodeToString(user.vault.getUser(user.userID).BridgePass)
|
||||
}
|
||||
|
||||
func (user *User) AuthUID() string {
|
||||
@ -51,7 +82,7 @@ func (user *User) SetKeyPass(keyPass []byte) error {
|
||||
})
|
||||
}
|
||||
|
||||
// SetAuth updates the auth secrets for the given user.
|
||||
// SetAuth sets the auth secrets for the given user.
|
||||
func (user *User) SetAuth(authUID, authRef string) error {
|
||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||
data.AuthUID = authUID
|
||||
@ -59,33 +90,23 @@ func (user *User) SetAuth(authUID, authRef string) error {
|
||||
})
|
||||
}
|
||||
|
||||
// SetGluonAuth updates the gluon ID and key for the given user.
|
||||
func (user *User) SetGluonAuth(gluonID string, gluonKey []byte) error {
|
||||
// SetAddressMode sets the address mode for the given user.
|
||||
func (user *User) SetAddressMode(mode AddressMode) error {
|
||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||
data.GluonID = gluonID
|
||||
data.GluonKey = gluonKey
|
||||
data.AddressMode = mode
|
||||
})
|
||||
}
|
||||
|
||||
// SetEventID updates the event ID for the given user.
|
||||
// SetEventID sets the event ID for the given user.
|
||||
func (user *User) SetEventID(eventID string) error {
|
||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||
data.EventID = eventID
|
||||
})
|
||||
}
|
||||
|
||||
// SetSync updates the sync state for the given user.
|
||||
// SetSync sets the sync state for the given user.
|
||||
func (user *User) SetSync(hasSync bool) error {
|
||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||
data.HasSync = hasSync
|
||||
})
|
||||
}
|
||||
|
||||
// Clear clears the secrets for the given user.
|
||||
func (user *User) Clear() error {
|
||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||
data.AuthUID = ""
|
||||
data.AuthRef = ""
|
||||
data.KeyPass = nil
|
||||
})
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/ProtonMail/gluon/imap"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -32,30 +33,48 @@ func TestUser(t *testing.T) {
|
||||
require.NoError(t, user2.SetSync(false))
|
||||
|
||||
// Set gluon data for user 1 and 2.
|
||||
require.NoError(t, user1.SetGluonAuth("gluonID1", []byte("gluonKey1")))
|
||||
require.NoError(t, user2.SetGluonAuth("gluonID2", []byte("gluonKey2")))
|
||||
require.NoError(t, user1.SetGluonID("addrID1", "gluonID1"))
|
||||
require.NoError(t, user2.SetGluonID("addrID2", "gluonID2"))
|
||||
require.NoError(t, user1.SetUIDValidity("addrID1", imap.UID(1)))
|
||||
require.NoError(t, user2.SetUIDValidity("addrID2", imap.UID(2)))
|
||||
|
||||
// List available users.
|
||||
require.ElementsMatch(t, []string{"userID1", "userID2"}, s.GetUserIDs())
|
||||
|
||||
// Check gluon information for user 1.
|
||||
gluonID1, ok := user1.GetGluonIDs()["addrID1"]
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "gluonID1", gluonID1)
|
||||
uidValidity1, ok := user1.GetUIDValidity("addrID1")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, imap.UID(1), uidValidity1)
|
||||
require.NotEmpty(t, user1.GluonKey())
|
||||
|
||||
// Get auth information for user 1.
|
||||
require.Equal(t, "userID1", user1.UserID())
|
||||
require.Equal(t, "user1", user1.Username())
|
||||
require.Equal(t, "gluonID1", user1.GluonID())
|
||||
require.Equal(t, []byte("gluonKey1"), user1.GluonKey())
|
||||
require.Equal(t, hex.EncodeToString([]byte("token")), user1.BridgePass())
|
||||
require.Equal(t, vault.CombinedMode, user1.AddressMode())
|
||||
require.Equal(t, "authUID1", user1.AuthUID())
|
||||
require.Equal(t, "authRef1", user1.AuthRef())
|
||||
require.Equal(t, []byte("keyPass1"), user1.KeyPass())
|
||||
require.Equal(t, "eventID1", user1.EventID())
|
||||
require.Equal(t, true, user1.HasSync())
|
||||
|
||||
// Check gluon information for user 1.
|
||||
gluonID2, ok := user2.GetGluonIDs()["addrID2"]
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "gluonID2", gluonID2)
|
||||
uidValidity2, ok := user2.GetUIDValidity("addrID2")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, imap.UID(2), uidValidity2)
|
||||
require.NotEmpty(t, user2.GluonKey())
|
||||
|
||||
// Get auth information for user 2.
|
||||
require.Equal(t, "userID2", user2.UserID())
|
||||
require.Equal(t, "user2", user2.Username())
|
||||
require.Equal(t, "gluonID2", user2.GluonID())
|
||||
require.Equal(t, []byte("gluonKey2"), user2.GluonKey())
|
||||
require.Equal(t, hex.EncodeToString([]byte("token")), user2.BridgePass())
|
||||
require.Equal(t, vault.CombinedMode, user2.AddressMode())
|
||||
require.Equal(t, "authUID2", user2.AuthUID())
|
||||
require.Equal(t, "authRef2", user2.AuthRef())
|
||||
require.Equal(t, []byte("keyPass2"), user2.KeyPass())
|
||||
@ -63,8 +82,8 @@ func TestUser(t *testing.T) {
|
||||
require.Equal(t, false, user2.HasSync())
|
||||
|
||||
// Clear the users.
|
||||
require.NoError(t, user1.Clear())
|
||||
require.NoError(t, user2.Clear())
|
||||
require.NoError(t, s.ClearUser("userID1"))
|
||||
require.NoError(t, s.ClearUser("userID2"))
|
||||
|
||||
// Their secrets should now be cleared.
|
||||
require.Equal(t, "", user1.AuthUID())
|
||||
|
||||
@ -4,7 +4,6 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/fs"
|
||||
@ -12,6 +11,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ProtonMail/gluon/imap"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/certs"
|
||||
"github.com/bradenaw/juniper/xslices"
|
||||
)
|
||||
@ -99,16 +99,16 @@ func (vault *Vault) AddUser(userID, username, authUID, authRef string, keyPass [
|
||||
return nil, errors.New("user already exists")
|
||||
}
|
||||
|
||||
tok, err := RandomToken(16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vault.mod(func(data *Data) {
|
||||
data.Users = append(data.Users, UserData{
|
||||
UserID: userID,
|
||||
Username: username,
|
||||
BridgePass: hex.EncodeToString(tok),
|
||||
UserID: userID,
|
||||
Username: username,
|
||||
|
||||
GluonKey: newRandomToken(32),
|
||||
GluonIDs: make(map[string]string),
|
||||
UIDValidity: make(map[string]imap.UID),
|
||||
BridgePass: newRandomToken(16),
|
||||
AddressMode: CombinedMode,
|
||||
|
||||
AuthUID: authUID,
|
||||
AuthRef: authRef,
|
||||
@ -121,6 +121,14 @@ func (vault *Vault) AddUser(userID, username, authUID, authRef string, keyPass [
|
||||
return vault.GetUser(userID)
|
||||
}
|
||||
|
||||
func (vault *Vault) ClearUser(userID string) error {
|
||||
return vault.modUser(userID, func(data *UserData) {
|
||||
data.AuthUID = ""
|
||||
data.AuthRef = ""
|
||||
data.KeyPass = nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteUser removes the given user from the vault.
|
||||
func (vault *Vault) DeleteUser(userID string) error {
|
||||
return vault.mod(func(data *Data) {
|
||||
|
||||
Reference in New Issue
Block a user