witty/cmd/user.go
2022-01-28 21:25:15 -05:00

190 lines
3.5 KiB
Go

package cmd
import (
"bytes"
"crypto/sha256"
"encoding/json"
"fmt"
"log"
"os"
"github.com/dchest/uniuri"
"golang.org/x/term"
)
const (
userFileName = "./user.db"
)
type UserRecord struct {
User []byte `json:"Username"`
Seed []byte `json:"Seed"`
Passwd [32]byte `json:"Password"`
}
func hashPassword(seed []byte, passwd []byte) [32]byte {
input := append(seed, passwd...)
return sha256.Sum256(input)
}
func addUser(username []byte, passwd []byte) {
var users []UserRecord
var err error
seed := []byte(uniuri.NewLen(64))
hashed := hashPassword(seed, passwd)
exist := false
file, err := os.ReadFile(userFileName)
if err != nil {
goto nonexist
}
if err = json.Unmarshal(file, &users); err != nil {
log.Println("Failed to unmarsh file", err)
goto nonexist
}
// update the existing user if it exists
for i, u := range users {
if bytes.Equal(u.User, username) {
users[i].Seed = seed
users[i].Passwd = hashed
exist = true
break
}
}
nonexist:
if !exist {
users = append(users, UserRecord{username, seed, hashed})
}
output, err := json.Marshal(users)
if err != nil {
log.Println("Failed to marshal passwords", err)
return
}
os.WriteFile(userFileName, output, 0660)
}
func AddUser(username string) {
fmt.Println("Please type your password (it will not be echoed back):")
passwd, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
log.Println("Failed to read password", err)
return
}
if len(passwd) < 12 {
fmt.Println("Password too short, at least 12 bytes")
return
}
fmt.Println("Please type your password again:")
passwd2, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
log.Println("Failed to read password", err)
return
}
if !bytes.Equal(passwd, passwd2) {
fmt.Println("Password mismatch, try again")
return
}
addUser([]byte(username), passwd)
}
func DelUser(username string) {
var users []UserRecord
var err error
exist := false
file, err := os.ReadFile(userFileName)
if err != nil {
log.Println("Failed to read users file", err)
return
}
err = json.Unmarshal(file, &users)
if err != nil {
log.Println("Failed to parse json format", err)
return
}
// update the existing user if it exists
for i, u := range users {
if bytes.Equal(u.User, []byte(username)) {
users = append(users[:i], users[i+1:]...)
exist = true
break
}
}
if exist {
output, err := json.Marshal(users)
if err != nil {
log.Println("Failed to marshal passwords", err)
return
}
os.WriteFile(userFileName, output, 0660)
}
}
func ListUsers() {
var users []UserRecord
var err error
file, err := os.ReadFile(userFileName)
if err != nil {
log.Println("Failed to read users file", err)
return
}
err = json.Unmarshal(file, &users)
if err != nil {
log.Println("Failed to parse json format", err)
return
}
// update the existing user if it exists
fmt.Println("Users of the system:")
for _, u := range users {
fmt.Println(" ", string(u.User))
}
}
func ValidateUser(username []byte, passwd []byte) bool {
var users []UserRecord
var err error
file, err := os.ReadFile(userFileName)
if err != nil {
log.Println("Failed to read users file", err)
return false
}
err = json.Unmarshal(file, &users)
if err != nil {
log.Println("Failed to parse json format", err)
return false
}
// update the existing user if it exists
for _, u := range users {
if bytes.Equal(u.User, []byte(username)) {
hashed := hashPassword(u.Seed, passwd)
return bytes.Equal(hashed[:], u.Passwd[:])
}
}
return false
}