Finish adding simple user authentication
This commit is contained in:
parent
7009b0eda8
commit
9c442254a7
@ -22,6 +22,8 @@ type AsinkGlobals struct {
|
|||||||
storage Storage
|
storage Storage
|
||||||
server string
|
server string
|
||||||
port int
|
port int
|
||||||
|
username string
|
||||||
|
password string
|
||||||
}
|
}
|
||||||
|
|
||||||
var globals AsinkGlobals
|
var globals AsinkGlobals
|
||||||
@ -42,6 +44,12 @@ func init() {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
//make sure config file's permissions are read-write only for the current user
|
||||||
|
if !util.FileExistsAndHasPermissions(globals.configFileName, 384 /*0b110000000*/) {
|
||||||
|
fmt.Println("Error: Either the file at "+globals.configFileName+" doesn't exist, or it doesn't have permissions such that the current user is the only one allowed to read and write.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
config, err := conf.ReadConfigFile(globals.configFileName)
|
config, err := conf.ReadConfigFile(globals.configFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@ -73,8 +81,11 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO check errors on server settings
|
||||||
globals.server, err = config.GetString("server", "host")
|
globals.server, err = config.GetString("server", "host")
|
||||||
globals.port, err = config.GetInt("server", "port")
|
globals.port, err = config.GetInt("server", "port")
|
||||||
|
globals.username, err = config.GetString("server", "username")
|
||||||
|
globals.password, err = config.GetString("server", "password")
|
||||||
|
|
||||||
globals.db, err = GetAndInitDB(config)
|
globals.db, err = GetAndInitDB(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -15,6 +16,25 @@ import (
|
|||||||
const MIN_ERROR_WAIT = 100 // 1/10 of a second
|
const MIN_ERROR_WAIT = 100 // 1/10 of a second
|
||||||
const MAX_ERROR_WAIT = 10000 // 10 seconds
|
const MAX_ERROR_WAIT = 10000 // 10 seconds
|
||||||
|
|
||||||
|
func AuthenticatedRequest(method, url, bodyType string, body io.Reader, username, password string) (*http.Response, error) {
|
||||||
|
client := &http.Client{}
|
||||||
|
req, err := http.NewRequest(method, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if bodyType != "" {
|
||||||
|
req.Header.Set("Content-Type", bodyType)
|
||||||
|
}
|
||||||
|
req.SetBasicAuth(username, password)
|
||||||
|
return client.Do(req)
|
||||||
|
}
|
||||||
|
func AuthenticatedGet(url string, username, password string) (*http.Response, error) {
|
||||||
|
return AuthenticatedRequest("GET", url, "", nil, username, password)
|
||||||
|
}
|
||||||
|
func AuthenticatedPost(url, bodyType string, body io.Reader, username, password string) (*http.Response, error) {
|
||||||
|
return AuthenticatedRequest("POST", url, bodyType, body, username, password)
|
||||||
|
}
|
||||||
|
|
||||||
func SendEvent(globals AsinkGlobals, event *asink.Event) error {
|
func SendEvent(globals AsinkGlobals, event *asink.Event) error {
|
||||||
url := "http://" + globals.server + ":" + strconv.Itoa(int(globals.port)) + "/events/"
|
url := "http://" + globals.server + ":" + strconv.Itoa(int(globals.port)) + "/events/"
|
||||||
|
|
||||||
@ -28,7 +48,7 @@ func SendEvent(globals AsinkGlobals, event *asink.Event) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//actually make the request
|
//actually make the request
|
||||||
resp, err := http.Post(url, "application/json", bytes.NewReader(b))
|
resp, err := AuthenticatedPost(url, "application/json", bytes.NewReader(b), globals.username, globals.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -80,7 +100,7 @@ func GetEvents(globals AsinkGlobals, events chan *asink.Event) {
|
|||||||
} else {
|
} else {
|
||||||
fullUrl = url + "0"
|
fullUrl = url + "0"
|
||||||
}
|
}
|
||||||
resp, err := http.Get(fullUrl)
|
resp, err := AuthenticatedGet(fullUrl, globals.username, globals.password)
|
||||||
|
|
||||||
//if error, perform exponential backoff (with maximum timeout)
|
//if error, perform exponential backoff (with maximum timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -29,7 +29,7 @@ type Event struct {
|
|||||||
Timestamp int64
|
Timestamp int64
|
||||||
Permissions os.FileMode
|
Permissions os.FileMode
|
||||||
Username string
|
Username string
|
||||||
Sharename string
|
Sharename string //TODO start differentiating between a users' different shares
|
||||||
LocalStatus EventStatus `json:"-"`
|
LocalStatus EventStatus `json:"-"`
|
||||||
LocalId int64 `json:"-"`
|
LocalId int64 `json:"-"`
|
||||||
InDB bool `json:"-"` //defaults to false. Omitted from json marshalling.
|
InDB bool `json:"-"` //defaults to false. Omitted from json marshalling.
|
||||||
|
@ -64,7 +64,7 @@ func GetAndInitDB() (*AsinkDB, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (adb *AsinkDB) DatabaseAddEvent(e *asink.Event) (err error) {
|
func (adb *AsinkDB) DatabaseAddEvent(u *User, e *asink.Event) (err error) {
|
||||||
adb.lock.Lock()
|
adb.lock.Lock()
|
||||||
tx, err := adb.db.Begin()
|
tx, err := adb.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -79,7 +79,7 @@ func (adb *AsinkDB) DatabaseAddEvent(e *asink.Event) (err error) {
|
|||||||
adb.lock.Unlock()
|
adb.lock.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
result, err := tx.Exec("INSERT INTO events (userid, type, path, hash, predecessor, timestamp, permissions) VALUES (?,?,?,?,?,?,?,?);", e.Type, e.Path, e.Hash, e.Predecessor, e.Timestamp, e.Permissions)
|
result, err := tx.Exec("INSERT INTO events (userid, type, path, hash, predecessor, timestamp, permissions) VALUES (?,?,?,?,?,?,?);", u.Id, e.Type, e.Path, e.Hash, e.Predecessor, e.Timestamp, e.Permissions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -97,13 +97,13 @@ func (adb *AsinkDB) DatabaseAddEvent(e *asink.Event) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (adb *AsinkDB) DatabaseRetrieveEvents(firstId uint64, maxEvents uint) (events []*asink.Event, err error) {
|
func (adb *AsinkDB) DatabaseRetrieveEvents(firstId uint64, maxEvents uint, u *User) (events []*asink.Event, err error) {
|
||||||
adb.lock.Lock()
|
adb.lock.Lock()
|
||||||
//make sure the database gets unlocked on return
|
//make sure the database gets unlocked on return
|
||||||
defer func() {
|
defer func() {
|
||||||
adb.lock.Unlock()
|
adb.lock.Unlock()
|
||||||
}()
|
}()
|
||||||
rows, err := adb.db.Query("SELECT id, type, path, hash, predecessor, timestamp, permissions FROM events WHERE id >= ? ORDER BY id ASC LIMIT ?;", firstId, maxEvents)
|
rows, err := adb.db.Query("SELECT id, type, path, hash, predecessor, timestamp, permissions FROM events WHERE userid = ? AND id >= ? ORDER BY id ASC LIMIT ?;", u.Id, firstId, maxEvents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -13,17 +13,17 @@ type LongPollGroup struct {
|
|||||||
|
|
||||||
type PollingManager struct {
|
type PollingManager struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
groups map[string]*LongPollGroup
|
groups map[int64]*LongPollGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
var pm *PollingManager
|
var pm *PollingManager
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
pm = new(PollingManager)
|
pm = new(PollingManager)
|
||||||
pm.groups = make(map[string]*LongPollGroup)
|
pm.groups = make(map[int64]*LongPollGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addPoller(uid string, channel *chan *asink.Event) {
|
func addPoller(uid int64, channel *chan *asink.Event) {
|
||||||
pm.lock.RLock()
|
pm.lock.RLock()
|
||||||
|
|
||||||
group := pm.groups[uid]
|
group := pm.groups[uid]
|
||||||
@ -57,7 +57,7 @@ func addPoller(uid string, channel *chan *asink.Event) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func broadcastToPollers(uid string, event *asink.Event) {
|
func broadcastToPollers(uid int64, event *asink.Event) {
|
||||||
//store off the long polling group we're trying to send to and remove
|
//store off the long polling group we're trying to send to and remove
|
||||||
//it from PollingManager.groups
|
//it from PollingManager.groups
|
||||||
pm.lock.Lock()
|
pm.lock.Lock()
|
||||||
|
@ -62,7 +62,7 @@ func rootHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
fmt.Fprintf(w, "You're probably looking for /events/")
|
fmt.Fprintf(w, "You're probably looking for /events/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEvents(w http.ResponseWriter, r *http.Request, nextEvent uint64) {
|
func getEvents(w http.ResponseWriter, r *http.Request, user *server.User, nextEvent uint64) {
|
||||||
var events []*asink.Event
|
var events []*asink.Event
|
||||||
var error_message string = ""
|
var error_message string = ""
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -86,7 +86,7 @@ func getEvents(w http.ResponseWriter, r *http.Request, nextEvent uint64) {
|
|||||||
w.Write(b)
|
w.Write(b)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
events, err := adb.DatabaseRetrieveEvents(nextEvent, 50)
|
events, err := adb.DatabaseRetrieveEvents(nextEvent, 50, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
error_message = err.Error()
|
error_message = err.Error()
|
||||||
@ -96,7 +96,7 @@ func getEvents(w http.ResponseWriter, r *http.Request, nextEvent uint64) {
|
|||||||
//long-poll if events is empty
|
//long-poll if events is empty
|
||||||
if len(events) == 0 {
|
if len(events) == 0 {
|
||||||
c := make(chan *asink.Event)
|
c := make(chan *asink.Event)
|
||||||
addPoller("aclindsa", &c) //TODO support more than one user
|
addPoller(user.Id, &c) //TODO support more than one share per user
|
||||||
e, ok := <-c
|
e, ok := <-c
|
||||||
if ok {
|
if ok {
|
||||||
events = append(events, e)
|
events = append(events, e)
|
||||||
@ -104,7 +104,7 @@ func getEvents(w http.ResponseWriter, r *http.Request, nextEvent uint64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func putEvents(w http.ResponseWriter, r *http.Request) {
|
func putEvents(w http.ResponseWriter, r *http.Request, user *server.User) {
|
||||||
var events asink.EventList
|
var events asink.EventList
|
||||||
var error_message string = ""
|
var error_message string = ""
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -138,7 +138,7 @@ func putEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, event := range events.Events {
|
for _, event := range events.Events {
|
||||||
err = adb.DatabaseAddEvent(event)
|
err = adb.DatabaseAddEvent(user, event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//TODO should probably do this in a way that the caller knows how many of these have failed and doesn't re-try sending ones that succeeded
|
//TODO should probably do this in a way that the caller knows how many of these have failed and doesn't re-try sending ones that succeeded
|
||||||
//i.e. add this to the return codes or something
|
//i.e. add this to the return codes or something
|
||||||
@ -148,7 +148,7 @@ func putEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastToPollers("aclindsa", events.Events[0]) //TODO support more than one user
|
broadcastToPollers(user.Id, events.Events[0]) //TODO support more than one user
|
||||||
}
|
}
|
||||||
|
|
||||||
func eventHandler(w http.ResponseWriter, r *http.Request) {
|
func eventHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -172,15 +172,15 @@ func eventHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
//TODO display error message here instead
|
//TODO display error message here instead
|
||||||
fmt.Printf("ERROR parsing " + sm[1] + "\n")
|
fmt.Printf("ERROR parsing " + sm[1] + "\n")
|
||||||
getEvents(w, r, 0)
|
getEvents(w, r, user, 0)
|
||||||
} else {
|
} else {
|
||||||
getEvents(w, r, i)
|
getEvents(w, r, user, i)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
getEvents(w, r, 0)
|
getEvents(w, r, user, 0)
|
||||||
}
|
}
|
||||||
} else if r.Method == "POST" {
|
} else if r.Method == "POST" {
|
||||||
putEvents(w, r)
|
putEvents(w, r, user)
|
||||||
} else {
|
} else {
|
||||||
apiresponse := asink.APIResponse{
|
apiresponse := asink.APIResponse{
|
||||||
Status: asink.ERROR,
|
Status: asink.ERROR,
|
||||||
|
@ -28,6 +28,14 @@ func EnsureDirExists(dir string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FileExistsAndHasPermissions(file string, mode os.FileMode) bool {
|
||||||
|
info, err := os.Stat(file)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.Mode().Perm() == mode
|
||||||
|
}
|
||||||
|
|
||||||
//TODO maybe this shouldn't fail silently?
|
//TODO maybe this shouldn't fail silently?
|
||||||
func RecursiveRemoveEmptyDirs(dir string) {
|
func RecursiveRemoveEmptyDirs(dir string) {
|
||||||
var err error = nil
|
var err error = nil
|
||||||
|
Loading…
Reference in New Issue
Block a user