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