~emersion/soju

fa16337d97f9e2edbae2860470f2427a4847821d — Simon Ser a month ago b3e136e
Switch DB API to user IDs

This commit changes the Network schema to use user IDs instead of
usernames. While at it, a new UNIQUE(user, name) constraint ensures
there is no conflict with custom network names.

Closes: https://todo.sr.ht/~emersion/soju/86
References: https://todo.sr.ht/~emersion/soju/29
5 files changed, 54 insertions(+), 22 deletions(-)

M contrib/znc-import.go
M db.go
M downstream.go
M service.go
M user.go
M contrib/znc-import.go => contrib/znc-import.go +3 -2
@@ 114,8 114,9 @@ func main() {
		if err := db.StoreUser(u); err != nil {
			log.Fatalf("failed to store user %q: %v", username, err)
		}
		userID := u.ID

		l, err := db.ListNetworks(username)
		l, err := db.ListNetworks(userID)
		if err != nil {
			log.Fatalf("failed to list networks for user %q: %v", username, err)
		}


@@ 181,7 182,7 @@ func main() {
			n.Realname = netRealname
			n.Pass = pass

			if err := db.StoreNetwork(username, n); err != nil {
			if err := db.StoreNetwork(userID, n); err != nil {
				logger.Fatalf("failed to store network: %v", err)
			}


M db.go => db.go +42 -11
@@ 70,7 70,7 @@ CREATE TABLE User (
CREATE TABLE Network (
	id INTEGER PRIMARY KEY,
	name VARCHAR(255),
	user VARCHAR(255) NOT NULL,
	user INTEGER NOT NULL,
	addr VARCHAR(255) NOT NULL,
	nick VARCHAR(255) NOT NULL,
	username VARCHAR(255),


@@ 82,8 82,9 @@ CREATE TABLE Network (
	sasl_plain_password VARCHAR(255),
	sasl_external_cert BLOB DEFAULT NULL,
	sasl_external_key BLOB DEFAULT NULL,
	FOREIGN KEY(user) REFERENCES User(username),
	UNIQUE(user, addr, nick)
	FOREIGN KEY(user) REFERENCES User(id),
	UNIQUE(user, addr, nick),
	UNIQUE(user, name)
);

CREATE TABLE Channel (


@@ 115,6 116,36 @@ var migrations = []string{
		DROP TABLE User;
		ALTER TABLE UserNew RENAME TO User;
	`,
	`
		CREATE TABLE NetworkNew (
			id INTEGER PRIMARY KEY,
			name VARCHAR(255),
			user INTEGER NOT NULL,
			addr VARCHAR(255) NOT NULL,
			nick VARCHAR(255) NOT NULL,
			username VARCHAR(255),
			realname VARCHAR(255),
			pass VARCHAR(255),
			connect_commands VARCHAR(1023),
			sasl_mechanism VARCHAR(255),
			sasl_plain_username VARCHAR(255),
			sasl_plain_password VARCHAR(255),
			sasl_external_cert BLOB DEFAULT NULL,
			sasl_external_key BLOB DEFAULT NULL,
			FOREIGN KEY(user) REFERENCES User(id),
			UNIQUE(user, addr, nick),
			UNIQUE(user, name)
		);
		INSERT INTO NetworkNew
			SELECT Network.id, name, User.id as user, addr, nick,
				Network.username, realname, pass, connect_commands,
				sasl_mechanism, sasl_plain_username, sasl_plain_password,
				sasl_external_cert, sasl_external_key
			FROM Network
			JOIN User ON Network.user = User.username;
		DROP TABLE Network;
		ALTER TABLE NetworkNew RENAME TO Network;
	`,
}

type DB struct {


@@ 263,7 294,7 @@ func (db *DB) StoreUser(user *User) error {
	return err
}

func (db *DB) DeleteUser(username string) error {
func (db *DB) DeleteUser(id int64) error {
	db.lock.Lock()
	defer db.lock.Unlock()



@@ 279,17 310,17 @@ func (db *DB) DeleteUser(username string) error {
			FROM Channel
			JOIN Network ON Channel.network = Network.id
			WHERE Network.user = ?
		)`, username)
		)`, id)
	if err != nil {
		return err
	}

	_, err = tx.Exec("DELETE FROM Network WHERE user = ?", username)
	_, err = tx.Exec("DELETE FROM Network WHERE user = ?", id)
	if err != nil {
		return err
	}

	_, err = tx.Exec("DELETE FROM User WHERE username = ?", username)
	_, err = tx.Exec("DELETE FROM User WHERE id = ?", id)
	if err != nil {
		return err
	}


@@ 297,7 328,7 @@ func (db *DB) DeleteUser(username string) error {
	return tx.Commit()
}

func (db *DB) ListNetworks(username string) ([]Network, error) {
func (db *DB) ListNetworks(userID int64) ([]Network, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()



@@ 306,7 337,7 @@ func (db *DB) ListNetworks(username string) ([]Network, error) {
			sasl_external_cert, sasl_external_key
		FROM Network
		WHERE user = ?`,
		username)
		userID)
	if err != nil {
		return nil, err
	}


@@ 342,7 373,7 @@ func (db *DB) ListNetworks(username string) ([]Network, error) {
	return networks, nil
}

func (db *DB) StoreNetwork(username string, network *Network) error {
func (db *DB) StoreNetwork(userID int64, network *Network) error {
	db.lock.Lock()
	defer db.lock.Unlock()



@@ 385,7 416,7 @@ func (db *DB) StoreNetwork(username string, network *Network) error {
				realname, pass, connect_commands, sasl_mechanism, sasl_plain_username,
				sasl_plain_password, sasl_external_cert, sasl_external_key)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			username, netName, network.Addr, network.Nick, netUsername, realname, pass, connectCommands,
			userID, netName, network.Addr, network.Nick, netUsername, realname, pass, connectCommands,
			saslMechanism, saslPlainUsername, saslPlainPassword, network.SASL.External.CertBlob,
			network.SASL.External.PrivKeyBlob)
		if err != nil {

M downstream.go => downstream.go +2 -2
@@ 1016,7 1016,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
				return
			}
			n.Nick = nick
			err = dc.srv.db.StoreNetwork(dc.user.Username, &n.Network)
			err = dc.srv.db.StoreNetwork(dc.user.ID, &n.Network)
		})
		if err != nil {
			return err


@@ 1697,7 1697,7 @@ func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
	n.SASL.Mechanism = "PLAIN"
	n.SASL.Plain.Username = username
	n.SASL.Plain.Password = password
	if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
	if err := dc.srv.db.StoreNetwork(dc.user.ID, &n.Network); err != nil {
		dc.logger.Printf("failed to save NickServ credentials: %v", err)
	}
}

M service.go => service.go +4 -4
@@ 548,7 548,7 @@ func handleServiceCertfpGenerate(dc *downstreamConn, params []string) error {
	net.SASL.External.PrivKeyBlob = privKeyBytes
	net.SASL.Mechanism = "EXTERNAL"

	if err := dc.srv.db.StoreNetwork(net.Username, &net.Network); err != nil {
	if err := dc.srv.db.StoreNetwork(dc.user.ID, &net.Network); err != nil {
		return err
	}



@@ 593,7 593,7 @@ func handleServiceSASLSetPlain(dc *downstreamConn, params []string) error {
	net.SASL.Plain.Password = params[2]
	net.SASL.Mechanism = "PLAIN"

	if err := dc.srv.db.StoreNetwork(net.Username, &net.Network); err != nil {
	if err := dc.srv.db.StoreNetwork(dc.user.ID, &net.Network); err != nil {
		return err
	}



@@ 617,7 617,7 @@ func handleServiceSASLReset(dc *downstreamConn, params []string) error {
	net.SASL.External.PrivKeyBlob = nil
	net.SASL.Mechanism = ""

	if err := dc.srv.db.StoreNetwork(dc.user.Username, &net.Network); err != nil {
	if err := dc.srv.db.StoreNetwork(dc.user.ID, &net.Network); err != nil {
		return err
	}



@@ 689,7 689,7 @@ func handleUserDelete(dc *downstreamConn, params []string) error {

	u.stop()

	if err := dc.srv.db.DeleteUser(username); err != nil {
	if err := dc.srv.db.DeleteUser(dc.user.ID); err != nil {
		return fmt.Errorf("failed to delete user: %v", err)
	}


M user.go => user.go +3 -3
@@ 314,7 314,7 @@ func (u *user) getNetworkByID(id int64) *network {
func (u *user) run() {
	defer close(u.done)

	networks, err := u.srv.db.ListNetworks(u.Username)
	networks, err := u.srv.db.ListNetworks(u.ID)
	if err != nil {
		u.srv.Logger.Printf("failed to list networks for user %q: %v", u.Username, err)
		return


@@ 508,7 508,7 @@ func (u *user) createNetwork(record *Network) (*network, error) {
	}

	network := newNetwork(u, record, nil)
	err := u.srv.db.StoreNetwork(u.Username, &network.Network)
	err := u.srv.db.StoreNetwork(u.ID, &network.Network)
	if err != nil {
		return nil, err
	}


@@ 528,7 528,7 @@ func (u *user) updateNetwork(record *Network) (*network, error) {
		panic("tried updating a non-existing network")
	}

	if err := u.srv.db.StoreNetwork(u.Username, record); err != nil {
	if err := u.srv.db.StoreNetwork(u.ID, record); err != nil {
		return nil, err
	}