Changeset 625 in code


Ignore:
Timestamp:
Oct 12, 2021, 7:11:14 AM (4 years ago)
Author:
contact
Message:

service: allow updating other users

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/doc/soju.1.scd

    r620 r625  
    323323        Other options are:
    324324
    325         *-admin*
     325        *-admin* true|false
    326326                Make the new user an administrator.
    327327
     
    330330                realname set for a network.
    331331
    332 *user update* [-password <password>] [-realname <realname>]
    333         Update the current user. The options are the same as the _user create_
    334         command.
     332*user update* [username] [options...]
     333        Update a user. The options are the same as the _user create_ command.
     334
     335        If _username_ is omitted, the current user is updated. Only admins can
     336        update other users.
     337
     338        Not all flags are valid in all contexts:
     339
     340        - The _-username_ flag is never valid, usernames are immutable.
     341        - The _-realname_ flag is only valid when updating the current user.
     342        - The _-admin_ flag is only valid when updating another user.
    335343
    336344*user delete* <username>
  • trunk/service.go

    r615 r625  
    126126        }
    127127        if cmd.admin && !dc.user.Admin {
    128                 sendServicePRIVMSG(dc, fmt.Sprintf(`error: you must be an admin to use this command`))
     128                sendServicePRIVMSG(dc, "error: you must be an admin to use this command")
    129129                return
    130130        }
     
    767767}
    768768
     769func popArg(params []string) (string, []string) {
     770        if len(params) > 0 && !strings.HasPrefix(params[0], "-") {
     771                return params[0], params[1:]
     772        }
     773        return "", params
     774}
     775
    769776func handleUserUpdate(dc *downstreamConn, params []string) error {
    770777        var password, realname *string
     778        var admin *bool
    771779        fs := newFlagSet()
    772780        fs.Var(stringPtrFlag{&password}, "password", "")
    773781        fs.Var(stringPtrFlag{&realname}, "realname", "")
    774 
     782        fs.Var(boolPtrFlag{&admin}, "admin", "")
     783
     784        username, params := popArg(params)
    775785        if err := fs.Parse(params); err != nil {
    776786                return err
    777787        }
    778 
    779         // copy the user record because we'll mutate it
    780         record := dc.user.User
    781 
     788        if len(fs.Args()) > 0 {
     789                return fmt.Errorf("unexpected argument")
     790        }
     791
     792        var hashed *string
    782793        if password != nil {
    783                 hashed, err := bcrypt.GenerateFromPassword([]byte(*password), bcrypt.DefaultCost)
     794                hashedBytes, err := bcrypt.GenerateFromPassword([]byte(*password), bcrypt.DefaultCost)
    784795                if err != nil {
    785796                        return fmt.Errorf("failed to hash password: %v", err)
    786797                }
    787                 record.Password = string(hashed)
    788         }
    789         if realname != nil {
    790                 record.Realname = *realname
    791         }
    792 
    793         if err := dc.user.updateUser(&record); err != nil {
    794                 return err
    795         }
    796 
    797         sendServicePRIVMSG(dc, fmt.Sprintf("updated user %q", dc.user.Username))
     798                hashedStr := string(hashedBytes)
     799                hashed = &hashedStr
     800        }
     801
     802        if username != "" && username != dc.user.Username {
     803                if !dc.user.Admin {
     804                        return fmt.Errorf("you must be an admin to update other users")
     805                }
     806                if realname != nil {
     807                        return fmt.Errorf("cannot update -realname of other user")
     808                }
     809
     810                u := dc.srv.getUser(username)
     811                if u == nil {
     812                        return fmt.Errorf("unknown username %q", username)
     813                }
     814
     815                done := make(chan error, 1)
     816                u.events <- eventUserUpdate{
     817                        password: hashed,
     818                        admin:    admin,
     819                        done:     done,
     820                }
     821                if err := <-done; err != nil {
     822                        return err
     823                }
     824
     825                sendServicePRIVMSG(dc, fmt.Sprintf("updated user %q", username))
     826        } else {
     827                // copy the user record because we'll mutate it
     828                record := dc.user.User
     829
     830                if hashed != nil {
     831                        record.Password = *hashed
     832                }
     833                if realname != nil {
     834                        record.Realname = *realname
     835                }
     836                if admin != nil {
     837                        return fmt.Errorf("cannot update -admin of own user")
     838                }
     839
     840                if err := dc.user.updateUser(&record); err != nil {
     841                        return err
     842                }
     843
     844                sendServicePRIVMSG(dc, fmt.Sprintf("updated user %q", dc.user.Username))
     845        }
     846
    798847        return nil
    799848}
  • trunk/user.go

    r612 r625  
    5959
    6060type eventStop struct{}
     61
     62type eventUserUpdate struct {
     63        password *string
     64        admin    *bool
     65        done     chan error
     66}
    6167
    6268type deliveredClientMap map[string]string // client name -> msg ID
     
    643649                                dc.SendMessage(msg)
    644650                        })
     651                case eventUserUpdate:
     652                        // copy the user record because we'll mutate it
     653                        record := u.User
     654
     655                        if e.password != nil {
     656                                record.Password = *e.password
     657                        }
     658                        if e.admin != nil {
     659                                record.Admin = *e.admin
     660                        }
     661
     662                        e.done <- u.updateUser(&record)
     663
     664                        // If the password was updated, kill all downstream connections to
     665                        // force them to re-authenticate with the new credentials.
     666                        if e.password != nil {
     667                                u.forEachDownstream(func(dc *downstreamConn) {
     668                                        dc.Close()
     669                                })
     670                        }
    645671                case eventStop:
    646672                        u.forEachDownstream(func(dc *downstreamConn) {
Note: See TracChangeset for help on using the changeset viewer.