diff --git a/doc/soju.1.scd b/doc/soju.1.scd index 8dc65ae..94c0e1e 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -407,6 +407,9 @@ character. *-network* Select a network. By default, the current network is selected, if any. +*user status* + Show a list of users on this server. Only admins can query this information. + *user create* -username -password [options...] Create a new soju user. Only admin users can create new accounts. The _-username_ and _-password_ flags are mandatory. diff --git a/service.go b/service.go index 5960bd3..c12c156 100644 --- a/service.go +++ b/service.go @@ -272,6 +272,11 @@ func init() { }, "user": { children: serviceCommandSet{ + "status": { + desc: "show a list of users and their current status", + handle: handleUserStatus, + admin: true, + }, "create": { usage: "-username -password [-realname ] [-admin]", desc: "create a new soju user", @@ -874,6 +879,40 @@ func handleServiceSASLReset(ctx *serviceContext, params []string) error { return nil } +func handleUserStatus(ctx *serviceContext, params []string) error { + // Limit to a small amount of users to avoid sending + // thousands of messages on large instances. + users := make([]database.User, 0, 50) + + ctx.user.srv.lock.Lock() + n := len(ctx.user.srv.users) + for _, user := range ctx.user.srv.users { + if len(users) == cap(users) { + break + } + users = append(users, user.User) + } + ctx.user.srv.lock.Unlock() + + for _, user := range users { + line := user.Username + if user.Admin { + line += " (admin)" + } + networks, err := ctx.user.srv.db.ListNetworks(ctx, user.ID) + if err != nil { + return fmt.Errorf("could not get networks of user %q: %v", user.Username, err) + } + line += fmt.Sprintf(": %d networks", len(networks)) + ctx.print(line) + } + if n > len(users) { + ctx.print(fmt.Sprintf("(%d more users omitted)", n-len(users))) + } + + return nil +} + func handleUserCreate(ctx *serviceContext, params []string) error { fs := newFlagSet() username := fs.String("username", "", "")