Add support for the user create admin service command

This adds support for user create, a new service command only accessible
to admin users. This lets users create other users on the fly and makes
soju start the user routine immediately; unlike sojuctl which currently
requires closing soju, creating the user, and starting soju again.
This commit is contained in:
delthas 2020-06-07 01:30:27 +02:00 committed by Simon Ser
parent f5611ae3f9
commit 5be25711c7
3 changed files with 74 additions and 6 deletions

View File

@ -138,6 +138,12 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
Connect with the specified nickname. By default, the account's username
is used.
*network delete* <name>
Disconnect and delete a network.
*network status*
Show a list of saved networks and their current status.
*certfp generate* *[options...]* <network name>
Generate self-signed certificate and use it for authentication.
@ -159,15 +165,12 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
*certfp reset* <network name>
Disable SASL EXTERNAL authentication and remove stored certificate.
*user create* -username <username> -password <password> [-admin]
Create a new soju user. Only admin users can create new accounts.
*change-password* <new password>
Change current user password.
*network delete* <name>
Disconnect and delete a network.
*network status*
Show a list of saved networks and their current status.
# AUTHORS
Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other

View File

@ -88,6 +88,26 @@ func (s *Server) Run() error {
select {}
}
func (s *Server) createUser(user *User) (*user, error) {
s.lock.Lock()
defer s.lock.Unlock()
if _, ok := s.users[user.Username]; ok {
return nil, fmt.Errorf("user %q already exists", user.Username)
}
err := s.db.StoreUser(user)
if err != nil {
return nil, fmt.Errorf("could not create user in db: %v", err)
}
s.Logger.Printf("starting bouncer for new user %q", user.Username)
u := newUser(s, user)
s.users[u.Username] = u
go u.run()
return u, nil
}
func (s *Server) getUser(name string) *user {
s.lock.Lock()
u := s.users[name]

View File

@ -162,6 +162,17 @@ func init() {
},
},
},
"user": {
children: serviceCommandSet{
"create": {
usage: "-username <username> -password <password> [-admin]",
desc: "create a new soju user",
handle: handleUserCreate,
admin: true,
},
},
admin: true,
},
"change-password": {
usage: "<new password>",
desc: "change your password",
@ -567,3 +578,37 @@ func handlePasswordChange(dc *downstreamConn, params []string) error {
sendServicePRIVMSG(dc, "password updated")
return nil
}
func handleUserCreate(dc *downstreamConn, params []string) error {
fs := newFlagSet()
username := fs.String("username", "", "")
password := fs.String("password", "", "")
admin := fs.Bool("admin", false, "")
if err := fs.Parse(params); err != nil {
return err
}
if *username == "" {
return fmt.Errorf("flag -username is required")
}
if *password == "" {
return fmt.Errorf("flag -password is required")
}
hashed, err := bcrypt.GenerateFromPassword([]byte(*password), bcrypt.DefaultCost)
if err != nil {
return fmt.Errorf("failed to hash password: %v", err)
}
user := &User{
Username: *username,
Password: string(hashed),
Admin: *admin,
}
if _, err := dc.srv.createUser(user); err != nil {
return fmt.Errorf("could not create user: %v", err)
}
sendServicePRIVMSG(dc, fmt.Sprintf("created user %q", *username))
return nil
}