created session handler, session testing, implemented sessions to keep track of user authentication in the backend and implemented user logout api

This commit is contained in:
George Shaw 2017-10-31 18:05:48 -05:00
parent bdd6074f3f
commit 8408d1f23b
7 changed files with 198 additions and 3 deletions

View file

@ -51,6 +51,21 @@
</table>
</form>
<form class="api_form" method="POST" action="user_logout">
<table>
<thead>
<tr>
<th colspan="2">gPanel Logout</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2"><input type="submit" value="Logout"></td>
</tr>
</tbody>
</table>
</form>
<script type="text/javascript">
var form = document.getElementsByClassName('api_form');

View file

@ -18,6 +18,8 @@ func HandleAPI(path string, res http.ResponseWriter, req *http.Request) (bool, b
return true, UserAuthentication(res, req)
case "user_register":
return true, UserRegistration(res, req)
case "user_logout":
return true, UserLogout(res, req)
default:
return false, false
}

View file

@ -7,6 +7,7 @@ import (
"github.com/Ennovar/gPanel/pkg/database"
"github.com/Ennovar/gPanel/pkg/encryption"
"github.com/Ennovar/gPanel/pkg/networking"
)
// userRequestData struct is the structure of the JSON data to be
@ -57,6 +58,14 @@ func UserAuthentication(res http.ResponseWriter, req *http.Request) bool {
return false
}
store := networking.GetStore(networking.COOKIES_USER_AUTH)
err = store.Set(res, req, "auth", true, (60 * 60 * 24))
if err != nil {
http.Error(res, http.StatusText(500), http.StatusInternalServerError)
return false
}
res.WriteHeader(http.StatusNoContent)
return true
}
@ -107,3 +116,23 @@ func UserRegistration(res http.ResponseWriter, req *http.Request) bool {
res.WriteHeader(http.StatusNoContent)
return true
}
// UserRegistration function is accessed by an API call from the webhost root
// by accessing /user_logout and sending it an empty POST request. This function will
// delete the user-auth cookie session store
func UserLogout(res http.ResponseWriter, req *http.Request) bool {
if req.Method != "POST" {
http.Error(res, req.Method+" HTTP method is unsupported for this API.", http.StatusMethodNotAllowed)
return false
}
store := networking.GetStore(networking.COOKIES_USER_AUTH)
err := store.Delete(res, req)
if err != nil {
http.Error(res, http.StatusText(500), http.StatusInternalServerError)
return false
}
return true
}

View file

@ -0,0 +1,77 @@
// Package networking contains various functions used to communicate between networks and
// draw data from the client network.
package networking
import (
"net/http"
"github.com/gorilla/sessions"
)
var key = []byte("GbP=K4#f$khYuZpStK68GyHxGg$4@5K-")
const (
COOKIES_USER_AUTH = "gpanel-webhost-user-auth"
)
type store struct {
handle *sessions.CookieStore
cookieName string
}
func GetStore(name string) store {
sessionStore := store{
handle: sessions.NewCookieStore(key),
cookieName: name,
}
return sessionStore
}
func (s *store) Set(res http.ResponseWriter, req *http.Request, key string, value interface{}, expire int) error {
session, err := s.handle.Get(req, s.cookieName)
if err != nil {
http.Error(res, http.StatusText(500), http.StatusInternalServerError)
return err
}
session.Values[key] = value
session.Options = &sessions.Options{
Path: "/",
MaxAge: expire,
HttpOnly: true,
}
session.Save(req, res)
return nil
}
func (s *store) Read(res http.ResponseWriter, req *http.Request, key string) (interface{}, error) {
session, err := s.handle.Get(req, s.cookieName)
if err != nil {
http.Error(res, http.StatusText(500), http.StatusInternalServerError)
return nil, err
}
value := session.Values[key]
return value, nil
}
func (s *store) Delete(res http.ResponseWriter, req *http.Request) error {
session, err := s.handle.Get(req, s.cookieName)
if err != nil {
http.Error(res, http.StatusText(500), http.StatusInternalServerError)
return err
}
session.Options = &sessions.Options{
Path: "/",
MaxAge: -1,
HttpOnly: true,
}
session.Save(req, res)
return nil
}

View file

@ -0,0 +1,51 @@
// Package networking contains various functions used to communicate between networks and
// draw data from the client network.
package networking
import (
"net/http"
"net/http/httptest"
"reflect"
"testing"
)
// BUG(george-e-shaw-iv) Says statement coverage for network package is 0.0%, I think this
// has something to do with the fact that I'm trying to test methods appended to a struct.
func TestSessionStore(t *testing.T) {
storeData := []struct {
storeName string
cookieName string
key string
value interface{}
}{
{"test-store-one", "test-cookie-one", "foo", "bar"},
{"test-store-two", "test-cookie-two", "baz", true},
{"test-store-three", "test-cookie-three", "foobar", 32},
}
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
for _, data := range storeData {
store := GetStore(data.storeName)
err := store.Set(res, req, data.key, data.value, 60)
if err != nil {
t.Errorf("Error in session_store_test: %s", err.Error())
}
val, err := store.Read(res, req, data.key)
if err != nil {
t.Errorf("Error in session_store_test: %s", err.Error())
}
if reflect.TypeOf(data.value) != reflect.TypeOf(val) {
t.Errorf("Error in session_store_test type checks, expected %s, got %s", reflect.TypeOf(data.value), reflect.TypeOf(val))
}
err = store.Delete(res, req)
if err != nil {
t.Errorf("Error in session_store_test: %s", err.Error())
}
}
}))
defer testServer.Close()
}

15
pkg/webhost/check_auth.go Normal file
View file

@ -0,0 +1,15 @@
// Package webhost handles the logic of the webhosting panel
package webhost
import "strings"
var allowedUnauthorizedPathSuffixes = [...]string{"api_testing.html", "user_auth", "user_register"}
func CheckAuth(path string) bool {
for _, suffix := range allowedUnauthorizedPathSuffixes {
if strings.HasSuffix(path, suffix) {
return true
}
}
return false
}

View file

@ -8,18 +8,17 @@ import (
"github.com/Ennovar/gPanel/pkg/api"
"github.com/Ennovar/gPanel/pkg/logging"
"github.com/Ennovar/gPanel/pkg/networking"
"github.com/Ennovar/gPanel/pkg/routing"
)
type PrivateHost struct {
Auth int
Directory string
}
// NewPrivateHost returns a new PrivateHost type.
func NewPrivateHost() PrivateHost {
return PrivateHost{
Auth: 1,
Directory: "document_roots/webhost/",
}
}
@ -34,7 +33,14 @@ func (priv *PrivateHost) ServeHTTP(w http.ResponseWriter, req *http.Request) {
path = (priv.Directory + path)
}
if priv.Auth != 1 {
store := networking.GetStore(networking.COOKIES_USER_AUTH)
val, err := store.Read(w, req, "auth")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if val != true && !CheckAuth(path) {
routing.HttpThrowStatus(http.StatusUnauthorized, w)
logging.Console(logging.PRIVATE_PREFIX, logging.NORMAL_LOG, "Path \""+path+"\" rendered a 401 error.")
} else {