service: allow updating other users
This commit is contained in:
parent
96d4111be2
commit
4e9ddf78ab
@ -322,16 +322,24 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
|
|||||||
|
|
||||||
Other options are:
|
Other options are:
|
||||||
|
|
||||||
*-admin*
|
*-admin* true|false
|
||||||
Make the new user an administrator.
|
Make the new user an administrator.
|
||||||
|
|
||||||
*-realname* <realname>
|
*-realname* <realname>
|
||||||
Set the user's realname. This is used as a fallback if there is no
|
Set the user's realname. This is used as a fallback if there is no
|
||||||
realname set for a network.
|
realname set for a network.
|
||||||
|
|
||||||
*user update* [-password <password>] [-realname <realname>]
|
*user update* [username] [options...]
|
||||||
Update the current user. The options are the same as the _user create_
|
Update a user. The options are the same as the _user create_ command.
|
||||||
command.
|
|
||||||
|
If _username_ is omitted, the current user is updated. Only admins can
|
||||||
|
update other users.
|
||||||
|
|
||||||
|
Not all flags are valid in all contexts:
|
||||||
|
|
||||||
|
- The _-username_ flag is never valid, usernames are immutable.
|
||||||
|
- The _-realname_ flag is only valid when updating the current user.
|
||||||
|
- The _-admin_ flag is only valid when updating another user.
|
||||||
|
|
||||||
*user delete* <username>
|
*user delete* <username>
|
||||||
Delete a soju user. Only admins can delete accounts.
|
Delete a soju user. Only admins can delete accounts.
|
||||||
|
73
service.go
73
service.go
@ -125,7 +125,7 @@ func handleServicePRIVMSG(dc *downstreamConn, text string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cmd.admin && !dc.user.Admin {
|
if cmd.admin && !dc.user.Admin {
|
||||||
sendServicePRIVMSG(dc, fmt.Sprintf(`error: you must be an admin to use this command`))
|
sendServicePRIVMSG(dc, "error: you must be an admin to use this command")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,35 +766,84 @@ func handleUserCreate(dc *downstreamConn, params []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func popArg(params []string) (string, []string) {
|
||||||
|
if len(params) > 0 && !strings.HasPrefix(params[0], "-") {
|
||||||
|
return params[0], params[1:]
|
||||||
|
}
|
||||||
|
return "", params
|
||||||
|
}
|
||||||
|
|
||||||
func handleUserUpdate(dc *downstreamConn, params []string) error {
|
func handleUserUpdate(dc *downstreamConn, params []string) error {
|
||||||
var password, realname *string
|
var password, realname *string
|
||||||
|
var admin *bool
|
||||||
fs := newFlagSet()
|
fs := newFlagSet()
|
||||||
fs.Var(stringPtrFlag{&password}, "password", "")
|
fs.Var(stringPtrFlag{&password}, "password", "")
|
||||||
fs.Var(stringPtrFlag{&realname}, "realname", "")
|
fs.Var(stringPtrFlag{&realname}, "realname", "")
|
||||||
|
fs.Var(boolPtrFlag{&admin}, "admin", "")
|
||||||
|
|
||||||
|
username, params := popArg(params)
|
||||||
if err := fs.Parse(params); err != nil {
|
if err := fs.Parse(params); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(fs.Args()) > 0 {
|
||||||
|
return fmt.Errorf("unexpected argument")
|
||||||
|
}
|
||||||
|
|
||||||
// copy the user record because we'll mutate it
|
var hashed *string
|
||||||
record := dc.user.User
|
|
||||||
|
|
||||||
if password != nil {
|
if password != nil {
|
||||||
hashed, err := bcrypt.GenerateFromPassword([]byte(*password), bcrypt.DefaultCost)
|
hashedBytes, err := bcrypt.GenerateFromPassword([]byte(*password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to hash password: %v", err)
|
return fmt.Errorf("failed to hash password: %v", err)
|
||||||
}
|
}
|
||||||
record.Password = string(hashed)
|
hashedStr := string(hashedBytes)
|
||||||
}
|
hashed = &hashedStr
|
||||||
if realname != nil {
|
|
||||||
record.Realname = *realname
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dc.user.updateUser(&record); err != nil {
|
if username != "" && username != dc.user.Username {
|
||||||
return err
|
if !dc.user.Admin {
|
||||||
|
return fmt.Errorf("you must be an admin to update other users")
|
||||||
|
}
|
||||||
|
if realname != nil {
|
||||||
|
return fmt.Errorf("cannot update -realname of other user")
|
||||||
|
}
|
||||||
|
|
||||||
|
u := dc.srv.getUser(username)
|
||||||
|
if u == nil {
|
||||||
|
return fmt.Errorf("unknown username %q", username)
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan error, 1)
|
||||||
|
u.events <- eventUserUpdate{
|
||||||
|
password: hashed,
|
||||||
|
admin: admin,
|
||||||
|
done: done,
|
||||||
|
}
|
||||||
|
if err := <-done; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sendServicePRIVMSG(dc, fmt.Sprintf("updated user %q", username))
|
||||||
|
} else {
|
||||||
|
// copy the user record because we'll mutate it
|
||||||
|
record := dc.user.User
|
||||||
|
|
||||||
|
if hashed != nil {
|
||||||
|
record.Password = *hashed
|
||||||
|
}
|
||||||
|
if realname != nil {
|
||||||
|
record.Realname = *realname
|
||||||
|
}
|
||||||
|
if admin != nil {
|
||||||
|
return fmt.Errorf("cannot update -admin of own user")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dc.user.updateUser(&record); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sendServicePRIVMSG(dc, fmt.Sprintf("updated user %q", dc.user.Username))
|
||||||
}
|
}
|
||||||
|
|
||||||
sendServicePRIVMSG(dc, fmt.Sprintf("updated user %q", dc.user.Username))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
user.go
26
user.go
@ -59,6 +59,12 @@ type eventBroadcast struct {
|
|||||||
|
|
||||||
type eventStop struct{}
|
type eventStop struct{}
|
||||||
|
|
||||||
|
type eventUserUpdate struct {
|
||||||
|
password *string
|
||||||
|
admin *bool
|
||||||
|
done chan error
|
||||||
|
}
|
||||||
|
|
||||||
type deliveredClientMap map[string]string // client name -> msg ID
|
type deliveredClientMap map[string]string // client name -> msg ID
|
||||||
|
|
||||||
type deliveredStore struct {
|
type deliveredStore struct {
|
||||||
@ -642,6 +648,26 @@ func (u *user) run() {
|
|||||||
u.forEachDownstream(func(dc *downstreamConn) {
|
u.forEachDownstream(func(dc *downstreamConn) {
|
||||||
dc.SendMessage(msg)
|
dc.SendMessage(msg)
|
||||||
})
|
})
|
||||||
|
case eventUserUpdate:
|
||||||
|
// copy the user record because we'll mutate it
|
||||||
|
record := u.User
|
||||||
|
|
||||||
|
if e.password != nil {
|
||||||
|
record.Password = *e.password
|
||||||
|
}
|
||||||
|
if e.admin != nil {
|
||||||
|
record.Admin = *e.admin
|
||||||
|
}
|
||||||
|
|
||||||
|
e.done <- u.updateUser(&record)
|
||||||
|
|
||||||
|
// If the password was updated, kill all downstream connections to
|
||||||
|
// force them to re-authenticate with the new credentials.
|
||||||
|
if e.password != nil {
|
||||||
|
u.forEachDownstream(func(dc *downstreamConn) {
|
||||||
|
dc.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
case eventStop:
|
case eventStop:
|
||||||
u.forEachDownstream(func(dc *downstreamConn) {
|
u.forEachDownstream(func(dc *downstreamConn) {
|
||||||
dc.Close()
|
dc.Close()
|
||||||
|
Loading…
Reference in New Issue
Block a user