Add per-user realname setting
This allows users to set a default realname used if the per-network realname isn't set. A new "user update" command is introduced and can be extended to edit other user properties and other users in the future.
This commit is contained in:
parent
9a53d4cd08
commit
a14f646135
6
db.go
6
db.go
@ -30,6 +30,7 @@ type User struct {
|
||||
ID int64
|
||||
Username string
|
||||
Password string // hashed
|
||||
Realname string
|
||||
Admin bool
|
||||
}
|
||||
|
||||
@ -92,10 +93,13 @@ func (net *Network) GetUsername() string {
|
||||
return net.Nick
|
||||
}
|
||||
|
||||
func (net *Network) GetRealname() string {
|
||||
func GetRealname(user *User, net *Network) string {
|
||||
if net.Realname != "" {
|
||||
return net.Realname
|
||||
}
|
||||
if user.Realname != "" {
|
||||
return user.Realname
|
||||
}
|
||||
return net.Nick
|
||||
}
|
||||
|
||||
|
20
db_sqlite.go
20
db_sqlite.go
@ -16,7 +16,8 @@ CREATE TABLE User (
|
||||
id INTEGER PRIMARY KEY,
|
||||
username VARCHAR(255) NOT NULL UNIQUE,
|
||||
password VARCHAR(255),
|
||||
admin INTEGER NOT NULL DEFAULT 0
|
||||
admin INTEGER NOT NULL DEFAULT 0,
|
||||
realname VARCHAR(255)
|
||||
);
|
||||
|
||||
CREATE TABLE Network (
|
||||
@ -133,6 +134,7 @@ var sqliteMigrations = []string{
|
||||
`,
|
||||
"ALTER TABLE Channel ADD COLUMN detached_internal_msgid VARCHAR(255)",
|
||||
"ALTER TABLE Network ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1",
|
||||
"ALTER TABLE User ADD COLUMN realname VARCHAR(255)",
|
||||
}
|
||||
|
||||
type SqliteDB struct {
|
||||
@ -242,12 +244,13 @@ func (db *SqliteDB) GetUser(username string) (*User, error) {
|
||||
|
||||
user := &User{Username: username}
|
||||
|
||||
var password sql.NullString
|
||||
row := db.db.QueryRow("SELECT id, password, admin FROM User WHERE username = ?", username)
|
||||
if err := row.Scan(&user.ID, &password, &user.Admin); err != nil {
|
||||
var password, realname sql.NullString
|
||||
row := db.db.QueryRow("SELECT id, password, admin, realname FROM User WHERE username = ?", username)
|
||||
if err := row.Scan(&user.ID, &password, &user.Admin, &realname); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Password = password.String
|
||||
user.Realname = realname.String
|
||||
return user, nil
|
||||
}
|
||||
|
||||
@ -256,15 +259,16 @@ func (db *SqliteDB) StoreUser(user *User) error {
|
||||
defer db.lock.Unlock()
|
||||
|
||||
password := toNullString(user.Password)
|
||||
realname := toNullString(user.Realname)
|
||||
|
||||
var err error
|
||||
if user.ID != 0 {
|
||||
_, err = db.db.Exec("UPDATE User SET password = ?, admin = ? WHERE username = ?",
|
||||
password, user.Admin, user.Username)
|
||||
_, err = db.db.Exec("UPDATE User SET password = ?, admin = ?, realname = ? WHERE username = ?",
|
||||
password, user.Admin, realname, user.Username)
|
||||
} else {
|
||||
var res sql.Result
|
||||
res, err = db.db.Exec("INSERT INTO User(username, password, admin) VALUES (?, ?, ?)",
|
||||
user.Username, password, user.Admin)
|
||||
res, err = db.db.Exec("INSERT INTO User(username, password, admin, realname) VALUES (?, ?, ?, ?)",
|
||||
user.Username, password, user.Admin, realname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -160,7 +160,8 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
|
||||
Connect with the specified server password.
|
||||
|
||||
*-realname* <realname>
|
||||
Connect with the specified real name. By default, the nickname is used.
|
||||
Connect with the specified real name. By default, the account's realname
|
||||
is used if set, otherwise the network's nickname is used.
|
||||
|
||||
*-nick* <nickname>
|
||||
Connect with the specified nickname. By default, the account's username
|
||||
@ -296,9 +297,21 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
|
||||
*sasl reset* <network name>
|
||||
Disable SASL authentication and remove stored credentials.
|
||||
|
||||
*user create* -username <username> -password <password> [-admin]
|
||||
*user create* -username <username> -password <password> [options...]
|
||||
Create a new soju user. Only admin users can create new accounts.
|
||||
|
||||
Options:
|
||||
|
||||
*-admin*
|
||||
Make the new user an administrator.
|
||||
|
||||
*-realname* <realname>
|
||||
Set the user's realname. This is used as a fallback if there is no
|
||||
realname set for a network.
|
||||
|
||||
*user update* [-realname <realname>]
|
||||
Update the current user.
|
||||
|
||||
*user delete* <username>
|
||||
Delete a soju user. Only admins can delete accounts.
|
||||
|
||||
|
@ -83,8 +83,8 @@ func getNetworkAttrs(network *network) irc.Tags {
|
||||
if network.Username != "" {
|
||||
attrs["username"] = irc.TagValue(network.Username)
|
||||
}
|
||||
if network.Realname != "" {
|
||||
attrs["realname"] = irc.TagValue(network.Realname)
|
||||
if realname := GetRealname(&network.user.User, &network.Network); realname != "" {
|
||||
attrs["realname"] = irc.TagValue(realname)
|
||||
}
|
||||
|
||||
if u, err := network.URL(); err == nil {
|
||||
@ -1387,6 +1387,13 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the client just resets to the default, just wipe the per-network
|
||||
// preference
|
||||
storeRealname := realname
|
||||
if realname == dc.user.Realname {
|
||||
storeRealname = ""
|
||||
}
|
||||
|
||||
var storeErr error
|
||||
var needUpdate []Network
|
||||
dc.forEachNetwork(func(n *network) {
|
||||
@ -1398,7 +1405,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
||||
Params: []string{realname},
|
||||
})
|
||||
|
||||
n.Realname = realname
|
||||
n.Realname = storeRealname
|
||||
if err := dc.srv.db.StoreNetwork(dc.user.ID, &n.Network); err != nil {
|
||||
dc.logger.Printf("failed to store network realname: %v", err)
|
||||
storeErr = err
|
||||
@ -1407,7 +1414,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
||||
}
|
||||
|
||||
record := n.Network // copy network record because we'll mutate it
|
||||
record.Realname = realname
|
||||
record.Realname = storeRealname
|
||||
needUpdate = append(needUpdate, record)
|
||||
})
|
||||
|
||||
@ -2223,6 +2230,10 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
||||
realname, _ := attrs.GetTag("realname")
|
||||
pass, _ := attrs.GetTag("pass")
|
||||
|
||||
if realname == dc.user.Realname {
|
||||
realname = ""
|
||||
}
|
||||
|
||||
// TODO: reject unknown attributes
|
||||
|
||||
record := &Network{
|
||||
|
26
service.go
26
service.go
@ -254,11 +254,16 @@ func init() {
|
||||
"user": {
|
||||
children: serviceCommandSet{
|
||||
"create": {
|
||||
usage: "-username <username> -password <password> [-admin]",
|
||||
usage: "-username <username> -password <password> [-realname <realname>] [-admin]",
|
||||
desc: "create a new soju user",
|
||||
handle: handleUserCreate,
|
||||
admin: true,
|
||||
},
|
||||
"update": {
|
||||
usage: "[-realname <realname>]",
|
||||
desc: "update the current user",
|
||||
handle: handleUserUpdate,
|
||||
},
|
||||
"delete": {
|
||||
usage: "<username>",
|
||||
desc: "delete a user",
|
||||
@ -266,7 +271,6 @@ func init() {
|
||||
admin: true,
|
||||
},
|
||||
},
|
||||
admin: true,
|
||||
},
|
||||
"change-password": {
|
||||
usage: "<new password>",
|
||||
@ -751,6 +755,7 @@ func handleUserCreate(dc *downstreamConn, params []string) error {
|
||||
fs := newFlagSet()
|
||||
username := fs.String("username", "", "")
|
||||
password := fs.String("password", "", "")
|
||||
realname := fs.String("realname", "", "")
|
||||
admin := fs.Bool("admin", false, "")
|
||||
|
||||
if err := fs.Parse(params); err != nil {
|
||||
@ -771,6 +776,7 @@ func handleUserCreate(dc *downstreamConn, params []string) error {
|
||||
user := &User{
|
||||
Username: *username,
|
||||
Password: string(hashed),
|
||||
Realname: *realname,
|
||||
Admin: *admin,
|
||||
}
|
||||
if _, err := dc.srv.createUser(user); err != nil {
|
||||
@ -781,6 +787,22 @@ func handleUserCreate(dc *downstreamConn, params []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleUserUpdate(dc *downstreamConn, params []string) error {
|
||||
fs := newFlagSet()
|
||||
realname := fs.String("realname", "", "")
|
||||
|
||||
if err := fs.Parse(params); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dc.user.updateRealname(*realname); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendServicePRIVMSG(dc, fmt.Sprintf("updated user %q", dc.user.Username))
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleUserDelete(dc *downstreamConn, params []string) error {
|
||||
if len(params) != 1 {
|
||||
return fmt.Errorf("expected exactly one argument")
|
||||
|
@ -1671,7 +1671,7 @@ func (uc *upstreamConn) register() {
|
||||
uc.nick = uc.network.Nick
|
||||
uc.nickCM = uc.network.casemap(uc.nick)
|
||||
uc.username = uc.network.GetUsername()
|
||||
uc.realname = uc.network.GetRealname()
|
||||
uc.realname = GetRealname(&uc.user.User, &uc.network.Network)
|
||||
|
||||
uc.SendMessage(&irc.Message{
|
||||
Command: "CAP",
|
||||
|
30
user.go
30
user.go
@ -763,6 +763,12 @@ func (u *user) updateNetwork(record *Network) (*network, error) {
|
||||
panic("tried updating a new network")
|
||||
}
|
||||
|
||||
// If the realname is reset to the default, just wipe the per-network
|
||||
// setting
|
||||
if record.Realname == u.Realname {
|
||||
record.Realname = ""
|
||||
}
|
||||
|
||||
if err := u.checkNetwork(record); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -855,6 +861,30 @@ func (u *user) updatePassword(hashed string) error {
|
||||
return u.srv.db.StoreUser(&u.User)
|
||||
}
|
||||
|
||||
func (u *user) updateRealname(realname string) error {
|
||||
u.User.Realname = realname
|
||||
if err := u.srv.db.StoreUser(&u.User); err != nil {
|
||||
return fmt.Errorf("failed to update user %q: %v", u.Username, err)
|
||||
}
|
||||
|
||||
// Re-connect to networks which use the default realname
|
||||
var needUpdate []Network
|
||||
u.forEachNetwork(func(net *network) {
|
||||
if net.Realname == "" {
|
||||
needUpdate = append(needUpdate, net.Network)
|
||||
}
|
||||
})
|
||||
|
||||
var netErr error
|
||||
for _, net := range needUpdate {
|
||||
if _, err := u.updateNetwork(&net); err != nil {
|
||||
netErr = err
|
||||
}
|
||||
}
|
||||
|
||||
return netErr
|
||||
}
|
||||
|
||||
func (u *user) stop() {
|
||||
u.events <- eventStop{}
|
||||
<-u.done
|
||||
|
Loading…
Reference in New Issue
Block a user