Parcourir la source

Handle db better, fix bug, update db schema, rest

tags/v0.5.0
Daniel Oaks il y a 7 ans
Parent
révision
6d6c1936cc
6 fichiers modifiés avec 141 ajouts et 28 suppressions
  1. 2
    0
      CHANGELOG.md
  2. 57
    2
      irc/database.go
  3. 7
    7
      irc/registration.go
  4. 51
    17
      irc/rest_api.go
  5. 20
    2
      irc/server.go
  6. 4
    0
      oragono.go

+ 2
- 0
CHANGELOG.md Voir le fichier

11
 
11
 
12
 ### Added
12
 ### Added
13
 * Added ability to ban IP addresses and networks from the server with `DLINE`.
13
 * Added ability to ban IP addresses and networks from the server with `DLINE`.
14
+* Added REST API for use with web interface to manage accounts, DLINEs, etc.
14
 
15
 
15
 ### Changed
16
 ### Changed
17
+* Database upgraded to make handling accounts simpler.
16
 
18
 
17
 ### Removed
19
 ### Removed
18
 
20
 

+ 57
- 2
irc/database.go Voir le fichier

8
 	"fmt"
8
 	"fmt"
9
 	"log"
9
 	"log"
10
 	"os"
10
 	"os"
11
+	"strings"
11
 
12
 
12
 	"github.com/tidwall/buntdb"
13
 	"github.com/tidwall/buntdb"
13
 )
14
 )
15
 const (
16
 const (
16
 	// 'version' of the database schema
17
 	// 'version' of the database schema
17
 	keySchemaVersion = "db.version"
18
 	keySchemaVersion = "db.version"
19
+	// latest schema of the db
20
+	latestDbSchema = "2"
18
 	// key for the primary salt used by the ircd
21
 	// key for the primary salt used by the ircd
19
 	keySalt = "crypto.salt"
22
 	keySalt = "crypto.salt"
20
 )
23
 )
40
 		tx.Set(keySalt, encodedSalt, nil)
43
 		tx.Set(keySalt, encodedSalt, nil)
41
 
44
 
42
 		// set schema version
45
 		// set schema version
43
-		tx.Set(keySchemaVersion, "1", nil)
46
+		tx.Set(keySchemaVersion, "2", nil)
44
 		return nil
47
 		return nil
45
 	})
48
 	})
46
 
49
 
47
 	if err != nil {
50
 	if err != nil {
48
-		log.Fatal("Could not save bunt store:", err.Error())
51
+		log.Fatal("Could not save datastore:", err.Error())
49
 	}
52
 	}
50
 }
53
 }
51
 
54
 
52
 // UpgradeDB upgrades the datastore to the latest schema.
55
 // UpgradeDB upgrades the datastore to the latest schema.
53
 func UpgradeDB(path string) {
56
 func UpgradeDB(path string) {
57
+	store, err := buntdb.Open(path)
58
+	if err != nil {
59
+		log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
60
+	}
61
+	defer store.Close()
62
+
63
+	err = store.Update(func(tx *buntdb.Tx) error {
64
+		version, _ := tx.Get(keySchemaVersion)
65
+
66
+		// == version 1 -> 2 ==
67
+		// account key changes and account.verified key bugfix.
68
+		if version == "1" {
69
+			log.Println("Updating store v1 to v2")
70
+
71
+			var keysToRemove []string
72
+			newKeys := make(map[string]string)
73
+
74
+			tx.AscendKeys("account *", func(key, value string) bool {
75
+				keysToRemove = append(keysToRemove, key)
76
+				splitkey := strings.Split(key, " ")
77
+
78
+				// work around bug
79
+				if splitkey[2] == "exists" {
80
+					// manually create new verified key
81
+					newVerifiedKey := fmt.Sprintf("%s.verified %s", splitkey[0], splitkey[1])
82
+					newKeys[newVerifiedKey] = "1"
83
+				} else if splitkey[1] == "%s" {
84
+					return true
85
+				}
86
+
87
+				newKey := fmt.Sprintf("%s.%s %s", splitkey[0], splitkey[2], splitkey[1])
88
+				newKeys[newKey] = value
89
+
90
+				return true
91
+			})
92
+
93
+			for _, key := range keysToRemove {
94
+				tx.Delete(key)
95
+			}
96
+			for key, value := range newKeys {
97
+				tx.Set(key, value, nil)
98
+			}
99
+
100
+			tx.Set(keySchemaVersion, "2", nil)
101
+		}
102
+
103
+		return nil
104
+	})
105
+	if err != nil {
106
+		log.Fatal("Could not update datastore:", err.Error())
107
+	}
108
+
54
 	return
109
 	return
55
 }
110
 }

+ 7
- 7
irc/registration.go Voir le fichier

17
 )
17
 )
18
 
18
 
19
 const (
19
 const (
20
-	keyAccountExists      = "account %s exists"
21
-	keyAccountVerified    = "account %s verified"
22
-	keyAccountName        = "account %s name" // stores the 'preferred name' of the account, not casemapped
23
-	keyAccountRegTime     = "account %s registered.time"
24
-	keyAccountCredentials = "account %s credentials"
20
+	keyAccountExists      = "account.exists %s"
21
+	keyAccountVerified    = "account.verified %s"
22
+	keyAccountName        = "account.name %s" // stores the 'preferred name' of the account, not casemapped
23
+	keyAccountRegTime     = "account.registered.time %s"
24
+	keyAccountCredentials = "account.credentials %s"
25
 	keyCertToAccount      = "account.creds.certfp %s"
25
 	keyCertToAccount      = "account.creds.certfp %s"
26
 )
26
 )
27
 
27
 
80
 }
80
 }
81
 
81
 
82
 // removeFailedRegCreateData removes the data created by REG CREATE if the account creation fails early.
82
 // removeFailedRegCreateData removes the data created by REG CREATE if the account creation fails early.
83
-func removeFailedRegCreateData(store buntdb.DB, account string) {
83
+func removeFailedRegCreateData(store *buntdb.DB, account string) {
84
 	// error is ignored here, we can't do much about it anyways
84
 	// error is ignored here, we can't do much about it anyways
85
 	store.Update(func(tx *buntdb.Tx) error {
85
 	store.Update(func(tx *buntdb.Tx) error {
86
 		tx.Delete(fmt.Sprintf(keyAccountExists, account))
86
 		tx.Delete(fmt.Sprintf(keyAccountExists, account))
250
 	// automatically complete registration
250
 	// automatically complete registration
251
 	if callbackNamespace == "*" {
251
 	if callbackNamespace == "*" {
252
 		err = server.store.Update(func(tx *buntdb.Tx) error {
252
 		err = server.store.Update(func(tx *buntdb.Tx) error {
253
-			tx.Set(keyAccountVerified, "1", nil)
253
+			tx.Set(fmt.Sprintf(keyAccountVerified, casefoldedAccount), "1", nil)
254
 
254
 
255
 			// load acct info inside store tx
255
 			// load acct info inside store tx
256
 			account := ClientAccount{
256
 			account := ClientAccount{

+ 51
- 17
irc/rest_api.go Voir le fichier

8
 import (
8
 import (
9
 	"encoding/json"
9
 	"encoding/json"
10
 	"net/http"
10
 	"net/http"
11
+	"strconv"
11
 	"time"
12
 	"time"
12
 
13
 
13
 	"fmt"
14
 	"fmt"
14
 
15
 
15
 	"github.com/gorilla/mux"
16
 	"github.com/gorilla/mux"
17
+	"github.com/tidwall/buntdb"
16
 )
18
 )
17
 
19
 
18
 const restErr = "{\"error\":\"An unknown error occurred\"}"
20
 const restErr = "{\"error\":\"An unknown error occurred\"}"
21
 // way to do it, given how HTTP handlers dispatch and work.
23
 // way to do it, given how HTTP handlers dispatch and work.
22
 var restAPIServer *Server
24
 var restAPIServer *Server
23
 
25
 
24
-type restVersionResp struct {
26
+type restInfoResp struct {
27
+	ServerName  string `json:"server-name"`
28
+	NetworkName string `json:"network-name"`
29
+
25
 	Version string `json:"version"`
30
 	Version string `json:"version"`
26
 }
31
 }
27
 
32
 
36
 }
41
 }
37
 
42
 
38
 type restAcct struct {
43
 type restAcct struct {
39
-	Name         string
44
+	Name         string    `json:"name"`
40
 	RegisteredAt time.Time `json:"registered-at"`
45
 	RegisteredAt time.Time `json:"registered-at"`
41
-	Clients      int
46
+	Clients      int       `json:"clients"`
42
 }
47
 }
43
 
48
 
44
 type restAccountsResp struct {
49
 type restAccountsResp struct {
45
-	Accounts map[string]restAcct `json:"accounts"`
50
+	Verified map[string]restAcct `json:"verified"`
46
 }
51
 }
47
 
52
 
48
 type restRehashResp struct {
53
 type restRehashResp struct {
51
 	Time       time.Time `json:"time"`
56
 	Time       time.Time `json:"time"`
52
 }
57
 }
53
 
58
 
54
-func restVersion(w http.ResponseWriter, r *http.Request) {
55
-	rs := restVersionResp{
56
-		Version: SemVer,
59
+func restInfo(w http.ResponseWriter, r *http.Request) {
60
+	rs := restInfoResp{
61
+		Version:     SemVer,
62
+		ServerName:  restAPIServer.name,
63
+		NetworkName: restAPIServer.networkName,
57
 	}
64
 	}
58
 	b, err := json.Marshal(rs)
65
 	b, err := json.Marshal(rs)
59
 	if err != nil {
66
 	if err != nil {
91
 
98
 
92
 func restGetAccounts(w http.ResponseWriter, r *http.Request) {
99
 func restGetAccounts(w http.ResponseWriter, r *http.Request) {
93
 	rs := restAccountsResp{
100
 	rs := restAccountsResp{
94
-		Accounts: make(map[string]restAcct),
101
+		Verified: make(map[string]restAcct),
95
 	}
102
 	}
96
 
103
 
97
-	// get accts
98
-	for key, info := range restAPIServer.accounts {
99
-		rs.Accounts[key] = restAcct{
100
-			Name:         info.Name,
101
-			RegisteredAt: info.RegisteredAt,
102
-			Clients:      len(info.Clients),
103
-		}
104
-	}
104
+	// get accounts
105
+	err := restAPIServer.store.View(func(tx *buntdb.Tx) error {
106
+		tx.AscendKeys("account.exists *", func(key, value string) bool {
107
+			key = key[len("account.exists "):]
108
+			_, err := tx.Get(fmt.Sprintf(keyAccountVerified, key))
109
+			verified := err == nil
110
+			fmt.Println(fmt.Sprintf(keyAccountVerified, key))
111
+
112
+			// get other details
113
+			name, _ := tx.Get(fmt.Sprintf(keyAccountName, key))
114
+			regTimeStr, _ := tx.Get(fmt.Sprintf(keyAccountRegTime, key))
115
+			regTimeInt, _ := strconv.ParseInt(regTimeStr, 10, 64)
116
+			regTime := time.Unix(regTimeInt, 0)
117
+
118
+			var clients int
119
+			acct := restAPIServer.accounts[key]
120
+			if acct != nil {
121
+				clients = len(acct.Clients)
122
+			}
123
+
124
+			if verified {
125
+				rs.Verified[key] = restAcct{
126
+					Name:         name,
127
+					RegisteredAt: regTime,
128
+					Clients:      clients,
129
+				}
130
+			} else {
131
+				//TODO(dan): Add to unverified list
132
+			}
133
+
134
+			return true // true to continue I guess?
135
+		})
136
+
137
+		return nil
138
+	})
105
 
139
 
106
 	b, err := json.Marshal(rs)
140
 	b, err := json.Marshal(rs)
107
 	if err != nil {
141
 	if err != nil {
139
 
173
 
140
 	// GET methods
174
 	// GET methods
141
 	rg := r.Methods("GET").Subrouter()
175
 	rg := r.Methods("GET").Subrouter()
142
-	rg.HandleFunc("/version", restVersion)
176
+	rg.HandleFunc("/info", restInfo)
143
 	rg.HandleFunc("/status", restStatus)
177
 	rg.HandleFunc("/status", restStatus)
144
 	rg.HandleFunc("/dlines", restGetDLines)
178
 	rg.HandleFunc("/dlines", restGetDLines)
145
 	rg.HandleFunc("/accounts", restGetAccounts)
179
 	rg.HandleFunc("/accounts", restGetAccounts)

+ 20
- 2
irc/server.go Voir le fichier

9
 	"bufio"
9
 	"bufio"
10
 	"crypto/tls"
10
 	"crypto/tls"
11
 	"encoding/base64"
11
 	"encoding/base64"
12
+	"errors"
12
 	"fmt"
13
 	"fmt"
13
 	"log"
14
 	"log"
14
 	"net"
15
 	"net"
32
 
33
 
33
 	bannedFromServerMsg      = ircmsg.MakeMessage(nil, "", "ERROR", "You are banned from this server (%s)")
34
 	bannedFromServerMsg      = ircmsg.MakeMessage(nil, "", "ERROR", "You are banned from this server (%s)")
34
 	bannedFromServerBytes, _ = bannedFromServerMsg.Line()
35
 	bannedFromServerBytes, _ = bannedFromServerMsg.Line()
36
+
37
+	errDbOutOfDate = errors.New("Database schema is old.")
35
 )
38
 )
36
 
39
 
37
 // Limits holds the maximum limits for various things such as topic lengths
40
 // Limits holds the maximum limits for various things such as topic lengths
102
 	rehashSignal          chan os.Signal
105
 	rehashSignal          chan os.Signal
103
 	restAPI               *RestAPIConfig
106
 	restAPI               *RestAPIConfig
104
 	signals               chan os.Signal
107
 	signals               chan os.Signal
105
-	store                 buntdb.DB
108
+	store                 *buntdb.DB
106
 	whoWas                *WhoWasList
109
 	whoWas                *WhoWasList
107
 }
110
 }
108
 
111
 
194
 	if err != nil {
197
 	if err != nil {
195
 		log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
198
 		log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
196
 	}
199
 	}
197
-	server.store = *db
200
+	server.store = db
201
+
202
+	// check db version
203
+	err = server.store.View(func(tx *buntdb.Tx) error {
204
+		version, _ := tx.Get(keySchemaVersion)
205
+		if version != latestDbSchema {
206
+			log.Println(fmt.Sprintf("Database must be updated. Expected schema v%s, got v%s.", latestDbSchema, version))
207
+			return errDbOutOfDate
208
+		}
209
+		return nil
210
+	})
211
+	if err != nil {
212
+		// close the db
213
+		db.Close()
214
+		return nil
215
+	}
198
 
216
 
199
 	// load dlines
217
 	// load dlines
200
 	server.loadDLines()
218
 	server.loadDLines()

+ 4
- 0
oragono.go Voir le fichier

84
 	} else if arguments["run"].(bool) {
84
 	} else if arguments["run"].(bool) {
85
 		irc.Log.SetLevel(config.Server.Log)
85
 		irc.Log.SetLevel(config.Server.Log)
86
 		server := irc.NewServer(configfile, config)
86
 		server := irc.NewServer(configfile, config)
87
+		if server == nil {
88
+			log.Println("Could not load server")
89
+			return
90
+		}
87
 		if !arguments["--quiet"].(bool) {
91
 		if !arguments["--quiet"].(bool) {
88
 			log.Println(fmt.Sprintf("Oragono v%s running", irc.SemVer))
92
 			log.Println(fmt.Sprintf("Oragono v%s running", irc.SemVer))
89
 			defer log.Println(irc.SemVer, "exiting")
93
 			defer log.Println(irc.SemVer, "exiting")

Chargement…
Annuler
Enregistrer