diff --git a/account/assets/js/panelHandlers/security/filter_ip.js b/account/assets/js/panelHandlers/security/filter_ip.js new file mode 100644 index 0000000..1424596 --- /dev/null +++ b/account/assets/js/panelHandlers/security/filter_ip.js @@ -0,0 +1,22 @@ +var ipModal = jQuery('.ip-filter-modal'); + +jQuery('._js_ip-filter-form').on('submit', function(e){ + e.preventDefault(); + + var requestData = {}; + requestData["ip"] = jQuery(this).find('input[name="ip"]').val(); + requestData["type"] = jQuery(this).find('input[name="type"]').val(); + + var xhr = new XMLHttpRequest(); + xhr.open(jQuery(this).attr('method'), jQuery(this).attr('action'), true); + xhr.send(JSON.stringify(requestData)); + + xhr.onloadend = function() { + if (xhr.status == 204) { + listFilteredIPs(requestData["type"]); + } + else { + alert("Something went wrong trying to filter that IP, please contact your administrator if problem persists."); + } + } +}); diff --git a/account/assets/js/panelHandlers/security/ipFiltering.js b/account/assets/js/panelHandlers/security/ipFiltering.js deleted file mode 100644 index 329c948..0000000 --- a/account/assets/js/panelHandlers/security/ipFiltering.js +++ /dev/null @@ -1,22 +0,0 @@ -var ipModal = jQuery('.ip-filter-modal'); - -jQuery('._js_ip-filtering-open').on('click', function(e){ - e.preventDefault(); - - var title; - switch(jQuery(this).attr('data')) { - case "general": - title = "General"; - break; - case "maintenance": - title = "Maintenance Mode"; - break; - default: - alert("Error, refresh and try again. If problem persists contact server administrator."); - return; - } - title += " IP Filtering"; - - ipModal.find('.modal-title').html(title); - ipModal.modal('show'); -}); diff --git a/account/assets/js/panelHandlers/security/ip_list.js b/account/assets/js/panelHandlers/security/ip_list.js new file mode 100644 index 0000000..3505ed5 --- /dev/null +++ b/account/assets/js/panelHandlers/security/ip_list.js @@ -0,0 +1,66 @@ +var ipModal = jQuery('.ip-filter-modal'); + +jQuery('._js_ip-filtering-open').on('click', function(e){ + e.preventDefault(); + + var title; + switch(jQuery(this).attr('data')) { + case "block": + title = "Block"; + ipModal.find('input[name="type"]').attr('value', 'block'); + ipModal.find('#filterIPHelp').html("Filtering this IP under the block filter type will disallow access to the website for all modes."); + break; + case "maintenance": + title = "Maintenance"; + ipModal.find('input[name="type"]').attr('value', 'maintenance'); + ipModal.find('#filterIPHelp').html("Whitelisting this IP under the maintenance filter type will allow access to the website during maintenance mode."); + break; + default: + alert("Error, refresh and try again. If problem persists contact server administrator."); + return; + } + title = "IP Filtering - " + title; + + ipModal.find('.modal-title').html(title); + + listFilteredIPs(jQuery(this).attr('data')); + + ipModal.modal('show'); +}); + +function listFilteredIPs(type) { + ipModal.find('._js_currently-filtered-ips').html(''); + + var requestData = {} + requestData["type"] = type; + + var xhr = new XMLHttpRequest(); + xhr.open('POST', 'api/ip/list', true); + xhr.send(JSON.stringify(requestData)); + + xhr.onloadend = function() { + if(xhr.status == 200) { + if(xhr.response != undefined && xhr.response.length != 0) { + jsonResponse = JSON.parse(xhr.response) + console.log(xhr.response); + jQuery.each(jsonResponse, function(k, v) { + ipModal.find('._js_currently-filtered-ips').append("
  • "+v.ip+"
  • "); + }); + } + else { + ipModal.find('.modal-body').html("An error has occurred, please refresh. If problem persists contact your administrator."); + } + } + else if(xhr.status == 204) { + ipModal.find('._js_currently-filtered-ips').append("
  • No Filtered IPs Currently Exist.
  • "); + } + else { + if(xhr.response != undefined && xhr.response.length != 0) { + ipModal.find('.modal-body').html(xhr.response); + } + else { + ipModal.find('.modal-body').html("An error has occurred, please refresh. If problem persists contact your administrator."); + } + } + } +} diff --git a/account/gPanel.html b/account/gPanel.html index a7ba849..a665aeb 100644 --- a/account/gPanel.html +++ b/account/gPanel.html @@ -59,7 +59,18 @@ @@ -174,7 +185,8 @@ - + + diff --git a/pkg/api/bundle/list.go b/pkg/api/bundle/list.go index b8d744b..6720c77 100644 --- a/pkg/api/bundle/list.go +++ b/pkg/api/bundle/list.go @@ -17,7 +17,6 @@ func List(res http.ResponseWriter, req *http.Request, logger *log.Logger, bundle } if len(bundles) <= 0 { - logger.Println("no bundles :: http response returns no content") res.WriteHeader(http.StatusNoContent) return true } diff --git a/pkg/api/ip/filter.go b/pkg/api/ip/filter.go new file mode 100644 index 0000000..d3370cb --- /dev/null +++ b/pkg/api/ip/filter.go @@ -0,0 +1,59 @@ +// Package ip is an API that deals with the blocking and unblocking of IPs on public servers +package ip + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + + "github.com/Ennovar/gPanel/pkg/database" +) + +func Filter(res http.ResponseWriter, req *http.Request, logger *log.Logger, dir string) bool { + if req.Method != "POST" { + logger.Println(req.URL.Path + "::" + req.Method + "::" + strconv.Itoa(http.StatusMethodNotAllowed) + "::" + http.StatusText(http.StatusMethodNotAllowed)) + http.Error(res, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return false + } + + var blockIPRequestData struct { + IP string `json:"ip"` + Type string `json:"type"` + } + + err := json.NewDecoder(req.Body).Decode(&blockIPRequestData) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + if blockIPRequestData.Type != "maintenance" && blockIPRequestData.Type != "block" { + logger.Println(req.URL.Path + "::" + " filtered IP type is invalid") + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + ds, err := database.Open(dir + database.DB_MAIN) + if err != nil || ds == nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + defer ds.Close() + + var newIP database.Struct_Filtered_IP + newIP.IP = blockIPRequestData.IP + newIP.Type = blockIPRequestData.Type + + err = ds.NewFilteredIP(&newIP) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + res.WriteHeader(http.StatusNoContent) + return true +} diff --git a/pkg/api/ip/list.go b/pkg/api/ip/list.go new file mode 100644 index 0000000..2993a82 --- /dev/null +++ b/pkg/api/ip/list.go @@ -0,0 +1,67 @@ +package ip + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + + "github.com/Ennovar/gPanel/pkg/database" +) + +func List(res http.ResponseWriter, req *http.Request, logger *log.Logger, dir string) bool { + if req.Method != "POST" { + logger.Println(req.URL.Path + "::" + req.Method + "::" + strconv.Itoa(http.StatusMethodNotAllowed) + "::" + http.StatusText(http.StatusMethodNotAllowed)) + http.Error(res, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return false + } + + var listIPRequestData struct { + Type string `json:"type"` + } + + err := json.NewDecoder(req.Body).Decode(&listIPRequestData) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + if listIPRequestData.Type != "maintenance" && listIPRequestData.Type != "block" { + logger.Println(req.URL.Path + "::" + " filtered IP type is invalid") + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + ds, err := database.Open(dir + database.DB_MAIN) + if err != nil || ds == nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + defer ds.Close() + + list, err := ds.GetFilteredIPs(listIPRequestData.Type) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + if len(list) > 0 { + b, err := json.Marshal(list) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + res.WriteHeader(http.StatusOK) + res.Write(b) + + return true + } + + res.WriteHeader(http.StatusNoContent) + return true +} diff --git a/pkg/api/ip/unfilter.go b/pkg/api/ip/unfilter.go new file mode 100644 index 0000000..64c2ec5 --- /dev/null +++ b/pkg/api/ip/unfilter.go @@ -0,0 +1,7 @@ +package ip + +import "net/http" + +func Unfilter(res http.ResponseWriter, req *http.Request, dir string) bool { + return true +} diff --git a/pkg/database/database.go b/pkg/database/database.go index 1258827..e37e271 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -16,8 +16,18 @@ const ( // Bucket constants const ( - BUCKET_USERS = "users" - BUCKET_PORTS = "ports" + BUCKET_USERS = "users" + BUCKET_PORTS = "ports" + BUCKET_FILTERED_IPS = "filtered_ips" +) + +// Database Structs +type ( + Struct_Filtered_IP struct { + ID int `json:"id"` + Type string `json:"type"` + IP string `json:"ip"` + } ) // Error codes @@ -45,12 +55,16 @@ func Open(filepath string) (*Datastore, error) { // Ensure that all top-level buckets exist err = ds.handle.Update(func(tx *bolt.Tx) error { _, err := tx.CreateBucketIfNotExists([]byte(BUCKET_USERS)) - if err != nil { return err } - _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_PORTS)) + _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_PORTS)) + if err != nil { + return err + } + + _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_FILTERED_IPS)) if err != nil { return err } diff --git a/pkg/database/ip_filtering.go b/pkg/database/ip_filtering.go new file mode 100644 index 0000000..92d5784 --- /dev/null +++ b/pkg/database/ip_filtering.go @@ -0,0 +1,49 @@ +package database + +import ( + "encoding/binary" + "encoding/json" + + "github.com/boltdb/bolt" +) + +func (ds *Datastore) NewFilteredIP(newip *Struct_Filtered_IP) error { + return ds.handle.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(BUCKET_FILTERED_IPS)) + + id, _ := b.NextSequence() + newip.ID = int(id) + + buf, err := json.Marshal(newip) + if err != nil { + return err + } + + key := make([]byte, 8) + binary.BigEndian.PutUint64(key, uint64(newip.ID)) + + return b.Put(key, buf) + }) +} + +func (ds *Datastore) GetFilteredIPs(iptype string) (map[int]Struct_Filtered_IP, error) { + filtered := make(map[int]Struct_Filtered_IP) + var holder Struct_Filtered_IP + + ds.handle.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(BUCKET_FILTERED_IPS)) + c := b.Cursor() + + for k, v := c.First(); k != nil; k, v = c.Next() { + json.Unmarshal(v, &holder) + + if holder.Type == iptype { + filtered[holder.ID] = holder + } + } + + return nil + }) + + return filtered, nil +} diff --git a/pkg/gpaccount/apihandler.go b/pkg/gpaccount/apihandler.go index bd6e7b1..20079dd 100644 --- a/pkg/gpaccount/apihandler.go +++ b/pkg/gpaccount/apihandler.go @@ -5,6 +5,7 @@ import ( "net/http" "strings" + "github.com/Ennovar/gPanel/pkg/api/ip" logapi "github.com/Ennovar/gPanel/pkg/api/log" "github.com/Ennovar/gPanel/pkg/api/server" "github.com/Ennovar/gPanel/pkg/api/user" @@ -42,6 +43,10 @@ func (con *Controller) apiHandler(res http.ResponseWriter, req *http.Request) (b return true, logapi.Read(res, req, con.APILogger, con.Directory) case "/log/delete": return true, logapi.Truncate(res, req, con.APILogger, con.Directory) + case "/ip/list": + return true, ip.List(res, req, con.APILogger, con.Directory) + case "/ip/filter": + return true, ip.Filter(res, req, con.APILogger, con.Directory) default: return false, false } diff --git a/pkg/networking/ip_address.go b/pkg/networking/ip_address.go index c0e7dad..50e57fd 100644 --- a/pkg/networking/ip_address.go +++ b/pkg/networking/ip_address.go @@ -3,22 +3,35 @@ package networking import ( - "bytes" "net/http" + "regexp" + "strings" ) -// GetClientIP returns the current client's IP as an array of bytes. -// BUG(george-e-shaw-iv) Uses an external API -func GetClientIP() ([]byte, error) { - resp, err := http.Get("http://myexternalip.com/raw") +// GetClientIP returns the current client's IP +func GetClientIP(req *http.Request) string { + var addr string + regex := regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)`) - if err != nil { - return []byte{}, err + if fwd := req.Header.Get(http.CanonicalHeaderKey("X-Forwarded-For")); fwd != "" { + s := strings.Index(fwd, ", ") + + if s == -1 { + s = len(fwd) + } + + addr = fwd[:s] + } else if fwd := req.Header.Get(http.CanonicalHeaderKey("X-Real-IP")); fwd != "" { + addr = fwd + } else if fwd := req.Header.Get(http.CanonicalHeaderKey("Forwarded")); fwd != "" { + + if match := regex.FindStringSubmatch(fwd); len(match) > 1 { + addr = strings.Trim(match[1], `"`) + } + + } else { + addr = strings.Split(req.RemoteAddr, ":")[0] } - defer resp.Body.Close() - buf := new(bytes.Buffer) - buf.ReadFrom(resp.Body) - - return buf.Bytes(), nil + return addr } diff --git a/server/document_root/assets/js/panelHandlers/bundles/manage.js b/server/document_root/assets/js/panelHandlers/bundles/manage.js index e736850..3590427 100644 --- a/server/document_root/assets/js/panelHandlers/bundles/manage.js +++ b/server/document_root/assets/js/panelHandlers/bundles/manage.js @@ -19,10 +19,9 @@ jQuery('._js_bundles-manage').on('click', function(e){ else { manageBundlesModal.find('.modal-body').html("An error has occurred. Please try again. If problem persists contact server administrator.") } - manageBundlesModal.modal('show'); } else if(xhr.status == 204) { - manageBundlesModal.modal('show'); + manageBundlesModal.find('.modal-body').html("

    No bundles current exist on the server.

    ") } else { if(xhr.response != undefined && xhr.response.length != 0) { @@ -31,7 +30,8 @@ jQuery('._js_bundles-manage').on('click', function(e){ else { manageBundlesModal.find('.modal-body').html(xhr.status + " Error!") } - manageBundlesModal.modal('show'); } } + + manageBundlesModal.modal('show'); }); diff --git a/server/document_root/gPanel.html b/server/document_root/gPanel.html index 5d34e64..2d3ec54 100644 --- a/server/document_root/gPanel.html +++ b/server/document_root/gPanel.html @@ -74,9 +74,7 @@ - +