Add single-use mod passwords
Single-use moderator passwords can only be generated by an admin with the /modpass command. To redeem the password and gain moderator privileges, a user just needs to call /auth with the password. The passwords are generated using the same function as the admin password. Additionally, generating passwords now uses crypto/rand instead of math/rand. Resolves #15
This commit is contained in:
parent
5516313c79
commit
1cd490b04a
|
@ -65,15 +65,21 @@ func init() {
|
|||
|
||||
pw := html.UnescapeString(strings.Join(args, " "))
|
||||
|
||||
//fmt.Printf("/auth from %s. expecting %q [%X], received %q [%X]\n", cl.name, settings.AdminPassword, settings.AdminPassword, pw, pw)
|
||||
if settings.AdminPassword == pw {
|
||||
cl.IsMod = true
|
||||
cl.IsAdmin = true
|
||||
fmt.Printf("[auth] %s used the admin password\n", cl.name)
|
||||
return "Admin rights granted."
|
||||
}
|
||||
|
||||
// Don't let on that this command exists. Not the most secure, but should be "good enough" LUL.
|
||||
return "Invalid command."
|
||||
if cl.belongsTo.redeemModPass(pw) {
|
||||
cl.IsMod = true
|
||||
fmt.Printf("[auth] %s used a mod password\n", cl.name)
|
||||
return "Moderator privileges granted."
|
||||
}
|
||||
|
||||
fmt.Printf("[auth] %s gave an invalid password\n", cl.name)
|
||||
return "Invalid password."
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -222,6 +228,14 @@ func init() {
|
|||
},
|
||||
},
|
||||
|
||||
"modpass": Command{
|
||||
HelpText: "Generate a single-use mod password.",
|
||||
Function: func(cl *Client, args []string) string {
|
||||
password := cl.belongsTo.generateModPass()
|
||||
return "Single use password: " + password
|
||||
},
|
||||
},
|
||||
|
||||
//"reloadsettings": func(cl *Client, args []string) string {
|
||||
// return ""
|
||||
//},
|
||||
|
|
58
chatroom.go
58
chatroom.go
|
@ -31,6 +31,9 @@ type ChatRoom struct {
|
|||
queue chan string
|
||||
playing string
|
||||
playingLink string
|
||||
|
||||
modPasswords []string // single-use mod passwords
|
||||
modPasswordsMtx sync.Mutex
|
||||
}
|
||||
|
||||
//initializing the chatroom
|
||||
|
@ -406,3 +409,58 @@ func (cr *ChatRoom) getClient(name string) (*Client, error) {
|
|||
}
|
||||
return nil, fmt.Errorf("Client with that name not found.")
|
||||
}
|
||||
|
||||
func (cr *ChatRoom) generateModPass() string {
|
||||
defer cr.modPasswordsMtx.Unlock()
|
||||
cr.modPasswordsMtx.Lock()
|
||||
|
||||
pass, err := generatePass(time.Now().Unix())
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error generating moderator password: %s", err)
|
||||
}
|
||||
|
||||
// Make sure the password is unique
|
||||
for existsInSlice(cr.modPasswords, pass) {
|
||||
pass, err = generatePass(time.Now().Unix())
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error generating moderator password: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
cr.modPasswords = append(cr.modPasswords, pass)
|
||||
return pass
|
||||
}
|
||||
|
||||
func (cr *ChatRoom) redeemModPass(pass string) bool {
|
||||
if pass == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
defer cr.modPasswordsMtx.Unlock()
|
||||
cr.modPasswordsMtx.Lock()
|
||||
|
||||
if existsInSlice(cr.modPasswords, pass) {
|
||||
cr.modPasswords = removeFromSlice(cr.modPasswords, pass)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func removeFromSlice(slice []string, needle string) []string {
|
||||
slc := []string{}
|
||||
for _, item := range slice {
|
||||
if item != needle {
|
||||
slc = append(slc, item)
|
||||
}
|
||||
}
|
||||
return slc
|
||||
}
|
||||
|
||||
func existsInSlice(slice []string, needle string) bool {
|
||||
for _, item := range slice {
|
||||
if item == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
23
settings.go
23
settings.go
|
@ -1,10 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -62,23 +63,29 @@ func LoadSettings(filename string) (*Settings, error) {
|
|||
if s.MaxMessageCount == 0 {
|
||||
s.MaxMessageCount = 300
|
||||
} else if s.MaxMessageCount < 0 {
|
||||
return s, fmt.Errorf("the MaxMessageCount value must be greater than 0, given %d", s.MaxMessageCount)
|
||||
return s, fmt.Errorf("MaxMessageCount value must be greater than 0, given %d", s.MaxMessageCount)
|
||||
}
|
||||
|
||||
s.AdminPassword = generateAdminPass(time.Now().Unix())
|
||||
s.AdminPassword, err = generatePass(time.Now().Unix())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to generate admin password: %s", err)
|
||||
}
|
||||
fmt.Printf("Settings reloaded. New admin password: %s\n", s.AdminPassword)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func generateAdminPass(seed int64) string {
|
||||
func generatePass(seed int64) (string, error) {
|
||||
out := ""
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
//for i := 0; i < 20; i++ {
|
||||
for len(out) < 20 {
|
||||
out = fmt.Sprintf("%s%X", out, r.Int31())
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(15)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out = fmt.Sprintf("%s%X", out, num)
|
||||
}
|
||||
return out
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Settings) Save() error {
|
||||
|
|
Loading…
Reference in New Issue