-
Notifications
You must be signed in to change notification settings - Fork 177
/
database.go
139 lines (114 loc) · 3.14 KB
/
database.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
// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package irc
import (
"encoding/base64"
"fmt"
"log"
"os"
"strings"
"github.com/oragono/oragono/irc/passwd"
"github.com/tidwall/buntdb"
)
const (
// 'version' of the database schema
keySchemaVersion = "db.version"
// latest schema of the db
latestDbSchema = "2"
// key for the primary salt used by the ircd
keySalt = "crypto.salt"
)
// InitDB creates the database.
func InitDB(path string) {
// prepare kvstore db
//TODO(dan): fail if already exists instead? don't want to overwrite good data
os.Remove(path)
store, err := buntdb.Open(path)
if err != nil {
log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
}
defer store.Close()
err = store.Update(func(tx *buntdb.Tx) error {
// set base db salt
salt, err := passwd.NewSalt()
encodedSalt := base64.StdEncoding.EncodeToString(salt)
if err != nil {
log.Fatal("Could not generate cryptographically-secure salt for the user:", err.Error())
}
tx.Set(keySalt, encodedSalt, nil)
// set schema version
tx.Set(keySchemaVersion, "2", nil)
return nil
})
if err != nil {
log.Fatal("Could not save datastore:", err.Error())
}
}
// OpenDatabase returns an existing database, performing a schema version check.
func OpenDatabase(path string) (*buntdb.DB, error) {
// open data store
db, err := buntdb.Open(path)
if err != nil {
return nil, err
}
// check db version
err = db.View(func(tx *buntdb.Tx) error {
version, _ := tx.Get(keySchemaVersion)
if version != latestDbSchema {
return fmt.Errorf("Database must be updated. Expected schema v%s, got v%s", latestDbSchema, version)
}
return nil
})
if err != nil {
// close the db
db.Close()
return nil, err
}
return db, nil
}
// UpgradeDB upgrades the datastore to the latest schema.
func UpgradeDB(path string) {
store, err := buntdb.Open(path)
if err != nil {
log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
}
defer store.Close()
err = store.Update(func(tx *buntdb.Tx) error {
version, _ := tx.Get(keySchemaVersion)
// == version 1 -> 2 ==
// account key changes and account.verified key bugfix.
if version == "1" {
log.Println("Updating store v1 to v2")
var keysToRemove []string
newKeys := make(map[string]string)
tx.AscendKeys("account *", func(key, value string) bool {
keysToRemove = append(keysToRemove, key)
splitkey := strings.Split(key, " ")
// work around bug
if splitkey[2] == "exists" {
// manually create new verified key
newVerifiedKey := fmt.Sprintf("%s.verified %s", splitkey[0], splitkey[1])
newKeys[newVerifiedKey] = "1"
} else if splitkey[1] == "%s" {
return true
}
newKey := fmt.Sprintf("%s.%s %s", splitkey[0], splitkey[2], splitkey[1])
newKeys[newKey] = value
return true
})
for _, key := range keysToRemove {
tx.Delete(key)
}
for key, value := range newKeys {
tx.Set(key, value, nil)
}
tx.Set(keySchemaVersion, "2", nil)
}
return nil
})
if err != nil {
log.Fatal("Could not update datastore:", err.Error())
}
return
}