-
Notifications
You must be signed in to change notification settings - Fork 241
/
persistence.go
194 lines (160 loc) · 7.14 KB
/
persistence.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package pushnotificationserver
import (
"crypto/ecdsa"
"database/sql"
"strings"
"github.com/golang/protobuf/proto"
sqlite3 "github.com/mutecomm/go-sqlcipher"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/protobuf"
)
type Persistence interface {
// GetPushNotificationRegistrationByPublicKeyAndInstallationID retrieve a push notification registration from storage given a public key and installation id
GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error)
// GetPushNotificationRegistrationByPublicKey retrieve all the push notification registrations from storage given a public key
GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error)
// GetPushNotificationRegistrationPublicKeys return all the public keys stored
GetPushNotificationRegistrationPublicKeys() ([][]byte, error)
//GetPushNotificationRegistrationVersion returns the latest version or 0 for a given pk and installationID
GetPushNotificationRegistrationVersion(publicKey []byte, installationID string) (uint64, error)
// UnregisterPushNotificationRegistration unregister a given pk/installationID
UnregisterPushNotificationRegistration(publicKey []byte, installationID string, version uint64) error
// DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id
DeletePushNotificationRegistration(publicKey []byte, installationID string) error
// SavePushNotificationRegistration saves a push notification option to the db
SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error
// GetIdentity returns the server identity key
GetIdentity() (*ecdsa.PrivateKey, error)
// SaveIdentity saves the server identity key
SaveIdentity(*ecdsa.PrivateKey) error
// PushNotificationExists checks whether a push notification exists and inserts it otherwise
PushNotificationExists([]byte) (bool, error)
}
type SQLitePersistence struct {
db *sql.DB
}
func NewSQLitePersistence(db *sql.DB) Persistence {
return &SQLitePersistence{db: db}
}
func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error) {
var marshaledRegistration []byte
err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ? AND registration IS NOT NULL`, publicKey, installationID).Scan(&marshaledRegistration)
if err == sql.ErrNoRows {
return nil, nil
} else if err != nil {
return nil, err
}
registration := &protobuf.PushNotificationRegistration{}
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
return nil, err
}
return registration, nil
}
func (p *SQLitePersistence) GetPushNotificationRegistrationVersion(publicKey []byte, installationID string) (uint64, error) {
var version uint64
err := p.db.QueryRow(`SELECT version FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID).Scan(&version)
if err == sql.ErrNoRows {
return 0, nil
} else if err != nil {
return 0, err
}
return version, nil
}
type PushNotificationIDAndRegistration struct {
ID []byte
Registration *protobuf.PushNotificationRegistration
}
func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error) {
// TODO: check for a max number of keys
publicKeyArgs := make([]interface{}, 0, len(publicKeys))
for _, pk := range publicKeys {
publicKeyArgs = append(publicKeyArgs, pk)
}
inVector := strings.Repeat("?, ", len(publicKeys)-1) + "?"
rows, err := p.db.Query(`SELECT public_key,registration FROM push_notification_server_registrations WHERE registration IS NOT NULL AND public_key IN (`+inVector+`)`, publicKeyArgs...) // nolint: gosec
if err != nil {
return nil, err
}
defer rows.Close()
var registrations []*PushNotificationIDAndRegistration
for rows.Next() {
response := &PushNotificationIDAndRegistration{}
var marshaledRegistration []byte
err := rows.Scan(&response.ID, &marshaledRegistration)
if err != nil {
return nil, err
}
registration := &protobuf.PushNotificationRegistration{}
// Skip if there's no registration
if marshaledRegistration == nil {
continue
}
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
return nil, err
}
response.Registration = registration
registrations = append(registrations, response)
}
return registrations, nil
}
func (p *SQLitePersistence) GetPushNotificationRegistrationPublicKeys() ([][]byte, error) {
rows, err := p.db.Query(`SELECT public_key FROM push_notification_server_registrations WHERE registration IS NOT NULL`)
if err != nil {
return nil, err
}
defer rows.Close()
var publicKeys [][]byte
for rows.Next() {
var publicKey []byte
err := rows.Scan(&publicKey)
if err != nil {
return nil, err
}
publicKeys = append(publicKeys, publicKey)
}
return publicKeys, nil
}
func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error {
marshaledRegistration, err := proto.Marshal(registration)
if err != nil {
return err
}
_, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, publicKey, registration.InstallationId, registration.Version, marshaledRegistration)
return err
}
func (p *SQLitePersistence) UnregisterPushNotificationRegistration(publicKey []byte, installationID string, version uint64) error {
_, err := p.db.Exec(`UPDATE push_notification_server_registrations SET registration = NULL, version = ? WHERE public_key = ? AND installation_id = ?`, version, publicKey, installationID)
return err
}
func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey []byte, installationID string) error {
_, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID)
return err
}
func (p *SQLitePersistence) SaveIdentity(privateKey *ecdsa.PrivateKey) error {
_, err := p.db.Exec(`INSERT INTO push_notification_server_identity (private_key) VALUES (?)`, crypto.FromECDSA(privateKey))
return err
}
func (p *SQLitePersistence) GetIdentity() (*ecdsa.PrivateKey, error) {
var pkBytes []byte
err := p.db.QueryRow(`SELECT private_key FROM push_notification_server_identity LIMIT 1`).Scan(&pkBytes)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
pk, err := crypto.ToECDSA(pkBytes)
if err != nil {
return nil, err
}
return pk, nil
}
func (p *SQLitePersistence) PushNotificationExists(messageID []byte) (bool, error) {
_, err := p.db.Exec(`INSERT INTO push_notification_server_notifications VALUES (?)`, messageID)
if err != nil && err.(sqlite3.Error).ExtendedCode == sqlite3.ErrConstraintUnique {
return true, nil
} else if err != nil {
return false, err
}
return false, nil
}