Finish core funtionality for server user-administration tool
This commit is contained in:
parent
4d7d82ed94
commit
219eecbfda
@ -7,7 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"reflect"
|
||||
"net/rpc"
|
||||
)
|
||||
|
||||
type boolIsSetFlag struct {
|
||||
@ -66,10 +66,13 @@ func UserAdd(args []string) {
|
||||
user.Username = flags.Arg(0)
|
||||
user.PWHash = server.HashPassword(passwordOne)
|
||||
|
||||
fmt.Println(user)
|
||||
i := 99
|
||||
err = RPCCall("UserModifier.AddUser", user, &i)
|
||||
if err != nil {
|
||||
if _, ok := err.(rpc.ServerError); ok && err.Error() == server.DuplicateUsernameErr.Error() {
|
||||
fmt.Println("Error: "+err.Error())
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -83,10 +86,13 @@ func UserDel(args []string) {
|
||||
user := new(server.User)
|
||||
user.Username = args[0]
|
||||
|
||||
fmt.Println(user)
|
||||
i := 99
|
||||
err := RPCCall("UserModifier.RemoveUser", user, &i)
|
||||
if err != nil {
|
||||
if _, ok := err.(rpc.ServerError); ok && err.Error() == server.NoUserErr.Error() {
|
||||
fmt.Println("Error: "+err.Error())
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -106,8 +112,6 @@ func UserMod(args []string) {
|
||||
flags.BoolVar(&rpcargs.UpdateLogin, "l", false, "Change the user's username (short version)")
|
||||
flags.Parse(args)
|
||||
|
||||
//set the UpdateAdmin flag based on whether it was present on the command-line
|
||||
|
||||
if flags.NArg() != 1 {
|
||||
fmt.Println("Error: please supply a username (and only one)")
|
||||
os.Exit(1)
|
||||
@ -136,6 +140,7 @@ func UserMod(args []string) {
|
||||
rpcargs.Updated.PWHash = server.HashPassword(passwordOne)
|
||||
}
|
||||
|
||||
//set the UpdateRole flag based on whether it was present on the command-line
|
||||
rpcargs.UpdateRole = admin.IsSet
|
||||
if admin.Value {
|
||||
rpcargs.Updated.Role = server.ADMIN
|
||||
@ -143,11 +148,18 @@ func UserMod(args []string) {
|
||||
rpcargs.Updated.Role = server.NORMAL
|
||||
}
|
||||
|
||||
fmt.Println(rpcargs)
|
||||
if !rpcargs.UpdateRole && !rpcargs.UpdateLogin && !rpcargs.UpdatePassword {
|
||||
fmt.Println("What exactly are you modifying again?")
|
||||
return
|
||||
}
|
||||
|
||||
i := 99
|
||||
err := RPCCall("UserModifier.ModifyUser", rpcargs, &i)
|
||||
if err != nil {
|
||||
fmt.Println(reflect.TypeOf(err))
|
||||
if _, ok := err.(rpc.ServerError); ok && err.Error() == server.NoUserErr.Error() {
|
||||
fmt.Println("Error: "+err.Error())
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
)
|
||||
|
||||
type UserModifier int
|
||||
type UserModifier struct {
|
||||
adb *AsinkDB
|
||||
}
|
||||
|
||||
type UserModifierArgs struct {
|
||||
Current *User
|
||||
@ -18,32 +19,58 @@ type UserModifierArgs struct {
|
||||
}
|
||||
|
||||
func (u *UserModifier) AddUser(user *User, result *int) error {
|
||||
fmt.Println("adding user: ", user)
|
||||
ret := 0
|
||||
result = &ret
|
||||
return nil
|
||||
err := u.adb.DatabaseAddUser(user)
|
||||
if err != nil {
|
||||
*result = 1
|
||||
} else {
|
||||
*result = 0
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *UserModifier) ModifyUser(args *UserModifierArgs, result *int) error {
|
||||
fmt.Println("modifying user: ", args)
|
||||
fmt.Println("from: ", args.Current)
|
||||
fmt.Println("to: ", args.Updated)
|
||||
ret := 0
|
||||
result = &ret
|
||||
currentUser, err := u.adb.DatabaseGetUser(args.Current.Username)
|
||||
if err != nil {
|
||||
*result = 1
|
||||
return err
|
||||
}
|
||||
|
||||
if args.UpdateLogin {
|
||||
currentUser.Username = args.Updated.Username
|
||||
}
|
||||
if args.UpdateRole {
|
||||
currentUser.Role = args.Updated.Role
|
||||
}
|
||||
if args.UpdatePassword {
|
||||
currentUser.PWHash = args.Updated.PWHash
|
||||
}
|
||||
|
||||
err = u.adb.DatabaseUpdateUser(currentUser)
|
||||
if err != nil {
|
||||
*result = 1
|
||||
return err
|
||||
}
|
||||
|
||||
*result = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserModifier) RemoveUser(user *User, result *int) error {
|
||||
fmt.Println("removing user: ", user)
|
||||
ret := 0
|
||||
result = &ret
|
||||
return nil
|
||||
err := u.adb.DatabaseDeleteUser(user)
|
||||
if err != nil {
|
||||
*result = 1
|
||||
} else {
|
||||
*result = 0
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func StartRPC(townDown chan int) {
|
||||
defer func() { townDown <- 0 }() //the main thread waits for this to ensure the socket is closed
|
||||
func StartRPC(tornDown chan int, adb *AsinkDB) {
|
||||
defer func() { tornDown <- 0 }() //the main thread waits for this to ensure the socket is closed
|
||||
|
||||
usermod := new(UserModifier)
|
||||
usermod.adb = adb
|
||||
|
||||
rpc.Register(usermod)
|
||||
rpc.HandleHTTP()
|
||||
l, err := net.Listen("unix", "/tmp/asink.sock")
|
||||
|
@ -1,8 +1,8 @@
|
||||
package main
|
||||
package server
|
||||
|
||||
import (
|
||||
"asink"
|
||||
"asink/server"
|
||||
"errors"
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"sync"
|
||||
@ -13,6 +13,9 @@ type AsinkDB struct {
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
var DuplicateUsernameErr = errors.New("Username already exists")
|
||||
var NoUserErr = errors.New("User doesn't exist")
|
||||
|
||||
func GetAndInitDB() (*AsinkDB, error) {
|
||||
dbLocation := "asink-server.db" //TODO make me configurable
|
||||
|
||||
@ -46,7 +49,7 @@ func GetAndInitDB() (*AsinkDB, error) {
|
||||
}
|
||||
if !rows.Next() {
|
||||
//if this is false, it means no rows were returned
|
||||
tx.Exec("CREATE TABLE user (id INTEGER PRIMARY KEY ASC, username TEXT, pwhash TEXT, role INTEGER);")
|
||||
tx.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY ASC, username TEXT, pwhash TEXT, role INTEGER);")
|
||||
} else {
|
||||
rows.Close()
|
||||
}
|
||||
@ -116,7 +119,7 @@ func (adb *AsinkDB) DatabaseRetrieveEvents(firstId uint64, maxEvents uint) (even
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (adb *AsinkDB) DatabaseAddUser(u *server.User) (err error) {
|
||||
func (adb *AsinkDB) DatabaseAddUser(u *User) (err error) {
|
||||
adb.lock.Lock()
|
||||
tx, err := adb.db.Begin()
|
||||
if err != nil {
|
||||
@ -131,11 +134,24 @@ func (adb *AsinkDB) DatabaseAddUser(u *server.User) (err error) {
|
||||
adb.lock.Unlock()
|
||||
}()
|
||||
|
||||
result, err := tx.Exec("INSERT INTO users (username, pwhash, role) VALUES (?,?);", u.Username, u.PWHash, u.Role)
|
||||
//make sure the username we're switching to doesn't already exist in the database
|
||||
existingUsername := ""
|
||||
row := tx.QueryRow("SELECT username FROM users WHERE username == ?;", u.Username)
|
||||
err = row.Scan(&existingUsername)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
//keep going
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
return DuplicateUsernameErr
|
||||
}
|
||||
|
||||
result, err := tx.Exec("INSERT INTO users (username, pwhash, role) VALUES (?,?,?);", u.Username, u.PWHash, u.Role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
u.Id, err = result.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -144,23 +160,63 @@ func (adb *AsinkDB) DatabaseAddUser(u *server.User) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
u.Id = id
|
||||
return nil
|
||||
}
|
||||
|
||||
func (adb *AsinkDB) DatabaseGetUser(username string) (user *server.User, err error) {
|
||||
//set attributes for the user with the same Id as *u
|
||||
func (adb *AsinkDB) DatabaseUpdateUser(u *User) (err error) {
|
||||
adb.lock.Lock()
|
||||
tx, err := adb.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//make sure the transaction gets rolled back on error, and the database gets unlocked
|
||||
defer func() {
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
adb.lock.Unlock()
|
||||
}()
|
||||
|
||||
//make sure the username we're switching to doesn't already exist in the database
|
||||
existingUsername := ""
|
||||
row := tx.QueryRow("SELECT username FROM users WHERE username == ? AND id != ?;", u.Username, u.Id)
|
||||
err = row.Scan(&existingUsername)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
//keep going
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
return DuplicateUsernameErr
|
||||
}
|
||||
|
||||
_, err = tx.Exec("UPDATE users SET username=?, pwhash=?, role=? WHERE id=?;", u.Username, u.PWHash, u.Role, u.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (adb *AsinkDB) DatabaseGetUser(username string) (user *User, err error) {
|
||||
adb.lock.Lock()
|
||||
//make sure the database gets unlocked
|
||||
defer adb.lock.Unlock()
|
||||
|
||||
row := adb.db.QueryRow("SELECT id, username, pwhash, role FROM users WHERE username == ?;", username)
|
||||
|
||||
user = new(server.User)
|
||||
user = new(User)
|
||||
err = row.Scan(&user.Id, &user.Username, &user.PWHash, &user.Role)
|
||||
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return nil, nil
|
||||
return nil, NoUserErr
|
||||
case err != nil:
|
||||
return nil, err
|
||||
default:
|
||||
@ -168,4 +224,38 @@ func (adb *AsinkDB) DatabaseGetUser(username string) (user *server.User, err err
|
||||
}
|
||||
}
|
||||
|
||||
func (adb *AsinkDB) DatabaseDeleteUser(u *User) (err error) {
|
||||
adb.lock.Lock()
|
||||
tx, err := adb.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//make sure the transaction gets rolled back on error, and the database gets unlocked
|
||||
defer func() {
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
adb.lock.Unlock()
|
||||
}()
|
||||
|
||||
res, err := tx.Exec("DELETE FROM users WHERE username=?;", u.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rows == 0 {
|
||||
return NoUserErr
|
||||
} else if rows > 1 {
|
||||
return errors.New("Error: attempting to delete user by username, but more than row will be affected: " + u.Username)
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -18,7 +18,7 @@ import (
|
||||
//global variables
|
||||
var eventsRegexp *regexp.Regexp
|
||||
var port int = 8080
|
||||
var adb *AsinkDB
|
||||
var adb *server.AsinkDB
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
@ -29,7 +29,7 @@ func init() {
|
||||
|
||||
eventsRegexp = regexp.MustCompile("^/events/([0-9]+)$")
|
||||
|
||||
adb, err = GetAndInitDB()
|
||||
adb, err = server.GetAndInitDB()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -39,7 +39,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
rpcTornDown := make(chan int)
|
||||
go server.StartRPC(rpcTornDown)
|
||||
go server.StartRPC(rpcTornDown, adb)
|
||||
|
||||
http.HandleFunc("/", rootHandler)
|
||||
http.HandleFunc("/events", eventHandler)
|
||||
|
Loading…
Reference in New Issue
Block a user