// dbManager manages database connections, and communicates back and forth with the manager goroutine func dbManager(conf config.Config, dbLaunchChan chan struct{}, dbKillChan chan struct{}) { log.Println("db: starting...") // Attempt to open database connection, depending on configuration // sqlite if conf.Sqlite != nil { log.Println("db: sqlite:", conf.Sqlite.File) // Replace the home character to set path path := common.ExpandHomeDir(conf.Sqlite.File) // Set DSN data.DB = new(data.SqliteBackend) data.DB.DSN(path) // Set up the database if err := data.DB.Setup(); err != nil { log.Fatalf("db: could not set up database: %s", err.Error()) } // Verify database file exists and is ready if _, err := os.Stat(path); err != nil { log.Fatalf("db: database file does not exist: %s", conf.Sqlite.File) } // Open the database connection if err := data.DB.Open(); err != nil { log.Fatalf("db: could not open database: %s", err) } // TODO: temporary, create a test user data.NewUser("test", "test", data.RoleAdmin) } else { // Invalid config log.Fatalf("db: invalid database selected") } // Database set up, trigger manager that it's ready close(dbLaunchChan) // Trigger events via channel for { select { // Stop database manager case <-dbKillChan: // Close the database connection pool if err := data.DB.Close(); err != nil { log.Fatalf("db: could not close connection") } // Inform manager that shutdown is complete log.Println("db: stopped!") dbKillChan <- struct{}{} return } } }
// PostUsers creates a new user for the wavepipe API, and returns a HTTP status and JSON. func PostUsers(w http.ResponseWriter, r *http.Request) { // Retrieve render ren := context.Get(r, CtxRender).(*render.Render) // Attempt to retrieve user from context sessionUser := new(data.User) if tempUser := context.Get(r, CtxUser); tempUser != nil { sessionUser = tempUser.(*data.User) } else { // No sessionUser stored in context log.Println("api: no sessionUser stored in request context!") ren.JSON(w, 500, serverErr) return } // Output struct for users request out := UsersResponse{} // Check API version if version, ok := mux.Vars(r)["version"]; ok { // Check if this API call is supported in the advertised version if !apiVersionSet.Has(version) { ren.JSON(w, 400, errRes(400, "unsupported API version: "+version)) return } } // Only allow administrators to create users if sessionUser.RoleID < data.RoleAdmin { ren.JSON(w, 403, permissionErr) return } // Check for required username, password, and role parameters username := r.PostFormValue("username") if username == "" { ren.JSON(w, 400, errRes(400, "missing required parameter: username")) return } password := r.PostFormValue("password") if password == "" { ren.JSON(w, 400, errRes(400, "missing required parameter: password")) return } // Check for role ID role := r.PostFormValue("role") if role == "" { ren.JSON(w, 400, errRes(400, "missing required parameter: role")) return } // Ensure role is valid integer, and valid role roleID, err := strconv.Atoi(role) if err != nil || (roleID != data.RoleGuest && roleID != data.RoleUser && roleID != data.RoleAdmin) { ren.JSON(w, 400, errRes(400, "invalid integer role ID")) return } // Generate a new user using the input username, password, and role user, err := data.NewUser(username, password, roleID) if err != nil { log.Println(err) ren.JSON(w, 500, serverErr) return } // HTTP 200 OK with JSON out.Users = []data.User{*user} ren.JSON(w, 200, out) return }
// TestAuthenticate runs through the entire authentication process with a newly-created user func TestAuthenticate(t *testing.T) { // Load database configuration data.DB = new(data.SqliteBackend) data.DB.DSN("~/.config/wavepipe/wavepipe.db") if err := data.DB.Open(); err != nil { t.Fatalf("Could not open database connection: %s", err.Error()) } defer data.DB.Close() // Create a temporary user, remove it on return user, err := data.NewUser("auth_test", "auth_test", data.RoleGuest) if err != nil { t.Fatal(err) } defer user.Delete() // Table of bcrypt tests and expected output var bcryptTests = []struct { username string password string clientErr error }{ // No username {"", "auth_test", ErrNoUsername}, // No password {"auth_test", "", ErrNoPassword}, // Invalid user {"no_exist", "auth_test", ErrInvalidUsername}, // Invalid password {"auth_test", "bad_pass", ErrInvalidPassword}, // Correct credentials {"auth_test", "auth_test", nil}, } // Iterate all bcrypt tests and check for valid output for _, test := range bcryptTests { // Generate POST data postData := url.Values{} postData.Set("username", test.username) postData.Set("password", test.password) // Generate a HTTP request req, err := http.NewRequest("POST", "http://localhost:8080/api/v0/login", bytes.NewBufferString(postData.Encode())) if err != nil { t.Fatal(err) } // Set required headers req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Length", strconv.Itoa(len(postData.Encode()))) // Attempt authentication via bcrypt _, authSession, clientErr, serverErr := bcryptAuthenticate(req) // Check for nil session, since sessions are not generated on login (they are generated in the API) if authSession != nil { t.Fatalf("non-nil session: %v", authSession) } // Check for expected client error if clientErr != test.clientErr { t.Fatalf("mismatched clientErr: %v != %v", clientErr, test.clientErr) } // Check for no server errors if serverErr != nil { t.Fatal(err) } } // Create a temporary session, remove it on return session, err := user.CreateSession("auth_test") if err != nil { t.Fatal(err) } defer session.Delete() // Table of token tests and expected output var tokenTests = []struct { token string clientErr error }{ // No token {"", ErrNoToken}, // Invalid token {"some_token", ErrInvalidToken}, // Correct token {session.Key, nil}, } // Iterate all token tests and check for valid output for _, test := range tokenTests { // Generate a HTTP request req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:8080/api/v0/status?s=%s", test.token), nil) if err != nil { t.Fatal(err) } // Attempt authentication via token _, _, clientErr, serverErr := tokenAuthenticate(req) // Check for expected client error if clientErr != test.clientErr { t.Fatalf("mismatched clientErr: %v != %v", clientErr, test.clientErr) } // Check for no server errors if serverErr != nil { t.Fatal(err) } } }