diff --git a/account/assets/js/formHandlers/register.js b/account/assets/js/formHandlers/register.js deleted file mode 100644 index 074a719..0000000 --- a/account/assets/js/formHandlers/register.js +++ /dev/null @@ -1,27 +0,0 @@ -jQuery('#registerForm').on('submit', function(e){ - e.preventDefault(); - - var formData = {}; - for(var y = 0, yy = this.length; y < yy; y++) { - var input = this[y]; - if(input.name) { - formData[input.name] = input.value; - } - } - - var xhr = new XMLHttpRequest(); - xhr.open(jQuery(this).attr('method'), jQuery(this).attr('action'), true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.send(JSON.stringify(formData)); - - xhr.onloadend = function() { - if(xhr.status == 200 || xhr.status == 204) { - jQuery('.index-alert').html('Register Success: You may now login.'); - jQuery('.index-alert').removeClass('alert-danger').addClass('alert-success').removeClass('d-none'); - } - else { - jQuery('.index-alert').html("Register Error: " + xhr.response); - jQuery('.index-alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none'); - } - } -}); diff --git a/account/index.html b/account/index.html index 9178b8e..8a996ff 100644 --- a/account/index.html +++ b/account/index.html @@ -51,30 +51,6 @@ -
-
-
- -
-
- -
-
-
- -
-
- -
-
- -
-
- -
- Register is Temporary, for development purposes only. -
- @@ -95,8 +71,8 @@ + - diff --git a/pkg/api/bundle/create.go b/pkg/api/bundle/create.go index a6ae5f0..59d7f0c 100644 --- a/pkg/api/bundle/create.go +++ b/pkg/api/bundle/create.go @@ -9,6 +9,8 @@ import ( "strconv" "github.com/Ennovar/gPanel/pkg/database" + "github.com/Ennovar/gPanel/pkg/emailer" + "github.com/Ennovar/gPanel/pkg/encryption" "github.com/Ennovar/gPanel/pkg/file" "github.com/Ennovar/gPanel/pkg/gpaccount" ) @@ -24,6 +26,7 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund Name string `json:"name"` AccPort int `json:"account_port"` PubPort int `json:"public_port"` + Email string `json:"email"` } err := json.NewDecoder(req.Body).Decode(&createBundleRequestData) @@ -82,10 +85,9 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund ds, err := database.Open(newBundle + "/" + database.DB_MAIN) if err != nil { logger.Println(req.URL.Path + "::" + err.Error()) - http.Error(res, err.Error(), http.StatusBadRequest) + http.Error(res, err.Error(), http.StatusInternalServerError) return false } - defer ds.Close() var databaseBundlePorts struct { Account int `json:"account"` @@ -97,14 +99,75 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund err = ds.Put(database.BUCKET_PORTS, []byte("bundle_ports"), databaseBundlePorts) if err != nil { logger.Println(req.URL.Path + "::" + err.Error()) - http.Error(res, err.Error(), http.StatusBadRequest) + http.Error(res, err.Error(), http.StatusInternalServerError) return false } + var defaultBundleUser database.Struct_Users + + defaultBundleUser.Pass, err = encryption.HashPassword("root") + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + defaultBundleUser.Secret = "" + + err = ds.Put(database.BUCKET_USERS, []byte("root"), defaultBundleUser) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + ds.Close() + bundles[createBundleRequestData.Name] = gpaccount.New(newBundle+"/", databaseBundlePorts.Account, databaseBundlePorts.Public) _ = bundles[createBundleRequestData.Name].Start() _ = bundles[createBundleRequestData.Name].Public.Start() + ds, err = database.Open("server/" + database.DB_SETTINGS) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + defer ds.Close() + + var smtpSettings database.Struct_SMTP + + err = ds.Get(database.BUCKET_GENERAL, []byte("smtp"), &smtpSettings) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + mail, err := emailer.New(smtpSettings.Type, emailer.Credentials{ + Username: smtpSettings.Username, + Password: smtpSettings.Password, + Server: smtpSettings.Server, + Port: smtpSettings.Port, + }) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + msg := string("Your new gPanel Bundle has been successfully registered.\r\n\n" + + "Account Port: " + strconv.Itoa(createBundleRequestData.AccPort) + "\r\n" + + "Public Port: " + strconv.Itoa(createBundleRequestData.PubPort) + "\r\n\n" + + "Default account username: root\r\n" + + "Default account password: root") + + err = mail.SendSimple(createBundleRequestData.Email, "New gPanel Bundle - "+createBundleRequestData.Name, msg) + 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([]byte(createBundleRequestData.Name)) diff --git a/pkg/api/email/get_smtp.go b/pkg/api/email/get_smtp.go new file mode 100644 index 0000000..8944ca6 --- /dev/null +++ b/pkg/api/email/get_smtp.go @@ -0,0 +1,49 @@ +package email + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + + "github.com/Ennovar/gPanel/pkg/database" +) + +func GetSMTP(res http.ResponseWriter, req *http.Request, logger *log.Logger, dir string) bool { + if req.Method != "GET" { + 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 + } + + ds, err := database.Open(dir + database.DB_SETTINGS) + 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 smtpDbData database.Struct_SMTP + + err = ds.Get(database.BUCKET_GENERAL, []byte("smtp"), &smtpDbData) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + // Remove password + smtpDbData.Password = "" + + b, err := json.Marshal(smtpDbData) + 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 +} diff --git a/pkg/api/email/set_smtp.go b/pkg/api/email/set_smtp.go new file mode 100644 index 0000000..31e877a --- /dev/null +++ b/pkg/api/email/set_smtp.go @@ -0,0 +1,60 @@ +package email + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + + "github.com/Ennovar/gPanel/pkg/database" + "github.com/Ennovar/gPanel/pkg/emailer" +) + +func SetSMTP(res http.ResponseWriter, req *http.Request, logger *log.Logger, dir string) bool { + if req.Method != "POST" && req.Method != "UPDATE" { + 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 smtpRequestData database.Struct_SMTP + + err := json.NewDecoder(req.Body).Decode(&smtpRequestData) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + // Test Authentication + _, err = emailer.New(smtpRequestData.Type, emailer.Credentials{ + Username: smtpRequestData.Username, + Password: smtpRequestData.Password, + Server: smtpRequestData.Server, + Port: smtpRequestData.Port, + }) + + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + ds, err := database.Open(dir + database.DB_SETTINGS) + if err != nil || ds == nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + defer ds.Close() + + err = ds.Put(database.BUCKET_GENERAL, []byte("smtp"), smtpRequestData) + 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/database/database.go b/pkg/database/database.go index 9ddbfb2..70453ce 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -4,6 +4,7 @@ package database import ( "encoding/json" "errors" + "strings" "time" "github.com/boltdb/bolt" @@ -11,14 +12,19 @@ import ( // Database constants const ( - DB_MAIN = "datastore.db" + DB_MAIN = "datastore.db" + DB_SETTINGS = "settings.db" ) // Bucket constants const ( + // DB_MAIN BUCKETS BUCKET_USERS = "users" BUCKET_PORTS = "ports" BUCKET_FILTERED_IPS = "filtered_ips" + + // DB_SETTINGS BUCKETS + BUCKET_GENERAL = "general" ) // Error codes @@ -45,19 +51,28 @@ 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 + if strings.HasSuffix(filepath, DB_MAIN) { + _, err := tx.CreateBucketIfNotExists([]byte(BUCKET_USERS)) + if err != nil { + return err + } + + _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_PORTS)) + if err != nil { + return err + } + + _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_FILTERED_IPS)) + if err != nil { + return err + } } - _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_PORTS)) - if err != nil { - return err - } - - _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_FILTERED_IPS)) - if err != nil { - return err + if strings.HasSuffix(filepath, DB_SETTINGS) { + _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_GENERAL)) + if err != nil { + return err + } } return nil diff --git a/pkg/database/settings.go b/pkg/database/settings.go new file mode 100644 index 0000000..f77d8b3 --- /dev/null +++ b/pkg/database/settings.go @@ -0,0 +1,9 @@ +package database + +type Struct_SMTP struct { + Type string `json:"type"` + Username string `json:"username"` + Password string `json:"password"` + Server string `json:"server"` + Port int `json:"port"` +} diff --git a/pkg/emailer/emailer.go b/pkg/emailer/emailer.go new file mode 100644 index 0000000..70e43bb --- /dev/null +++ b/pkg/emailer/emailer.go @@ -0,0 +1,74 @@ +package emailer + +import ( + "errors" + "net/smtp" + "strconv" +) + +type Credentials struct { + Username string + Password string + Server string + Port int +} + +type Emailer struct { + auth smtp.Auth + cred Credentials +} + +func New(eType string, creds Credentials) (*Emailer, error) { + var a smtp.Auth + + switch eType { + case "crammd5": + a = smtp.CRAMMD5Auth(creds.Username, creds.Password) + default: + a = smtp.PlainAuth("", creds.Username, creds.Password, creds.Server) + } + + if a == nil { + return nil, errors.New("unable to authenticate") + } + + return &Emailer{ + auth: a, + cred: creds, + }, nil +} + +// SendSimple function will fill out the to/subject email headers for you and allow +// you to just input a string as the email body. +func (e *Emailer) SendSimple(to string, subject string, body string) error { + if e.auth == nil { + return errors.New("smtp server authentication has expired") + } + + m := []byte("To:" + to + "\r\n" + + "Subject:" + subject + "\r\n" + + "\r\n" + + body + "\r\n") + + err := smtp.SendMail(e.cred.Server+":"+strconv.Itoa(e.cred.Port), e.auth, e.cred.Username, []string{to}, m) + if err != nil { + return err + } + + return nil +} + +// SendCustom function will not fill out the to/subject email headers for you and allow +// you to send a completely custom message in the form of bytes. +func (e *Emailer) SendCustom(to string, msg []byte) error { + if e.auth == nil { + return errors.New("smtp server authentication has expired") + } + + err := smtp.SendMail(e.cred.Server+":"+strconv.Itoa(e.cred.Port), e.auth, e.cred.Username, []string{to}, msg) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/gpserver/apihandler.go b/pkg/gpserver/apihandler.go index 8fe994f..de646fc 100644 --- a/pkg/gpserver/apihandler.go +++ b/pkg/gpserver/apihandler.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/Ennovar/gPanel/pkg/api/bundle" + "github.com/Ennovar/gPanel/pkg/api/email" logapi "github.com/Ennovar/gPanel/pkg/api/log" "github.com/Ennovar/gPanel/pkg/api/server" "github.com/Ennovar/gPanel/pkg/api/user" @@ -86,6 +87,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 "/email/set_smtp": + return true, email.SetSMTP(res, req, con.APILogger, con.Directory) + case "/email/get_smtp": + return true, email.GetSMTP(res, req, con.APILogger, con.Directory) default: return false, false } diff --git a/server/document_root/assets/js/panelHandlers/settings/smtp.js b/server/document_root/assets/js/panelHandlers/settings/smtp.js new file mode 100644 index 0000000..9405d5a --- /dev/null +++ b/server/document_root/assets/js/panelHandlers/settings/smtp.js @@ -0,0 +1,68 @@ +var smtpModal = jQuery('.smtp-settings-modal'); + +jQuery('._js_smtp-credentials').on('click', function(e){ + e.preventDefault(); + + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'api/email/get_smtp', true); + xhr.send(); + + xhr.onloadend = function() { + if(xhr.status == 200) { + var resp = JSON.parse(xhr.response); + + if(resp["type"] == "crammd5") { + jQuery('#smtpType').val(resp["type"]).change(); + } + + jQuery('#smtpUsername').val(resp["username"]); + jQuery('#smtpServer').val(resp["server"]); + jQuery('#smtpPort').val(resp["port"]); + } + smtpModal.modal('show'); + } +}); + +jQuery('._js_smtp-settings-form').on('submit', function(e){ + e.preventDefault(); + + var flag = false; + jQuery(this).find('input').each(function(i){ + if(jQuery(this) && jQuery(this).val()) return true; + else { + flag = true; + return false; + } + }); + + if(flag) { + alert('All inputs need to be filled out.'); + return; + } + + var requestData = {}; + requestData["type"] = jQuery(this).find('#smtpType').val(); + requestData["username"] = jQuery(this).find('#smtpUsername').val(); + requestData["password"] = jQuery(this).find('#smtpPassword').val(); + requestData["server"] = jQuery(this).find('#smtpServer').val(); + requestData["port"] = parseInt(jQuery(this).find('#smtpPort').val()); + + var xhr = new XMLHttpRequest(); + xhr.open(jQuery(this).attr('method'), jQuery(this).attr('action'), true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.send(JSON.stringify(requestData)); + + xhr.onloadend = function() { + if(xhr.status == 204) { + alert("New SMTP Settings Connect Successfully and are Saved."); + } + else { + if(xhr.response != undefined && xhr.response.length != 0) { + alert('Error: ' + xhr.response); + } + else { + alert('An error has occurred, refresh and try again. If problem persists please contact your administrator.'); + } + } + } +}); diff --git a/server/document_root/gPanel.html b/server/document_root/gPanel.html index 386af20..23fb211 100644 --- a/server/document_root/gPanel.html +++ b/server/document_root/gPanel.html @@ -44,6 +44,11 @@ Bundle name must be unqiue in terms of your current bundle collection. +
+ + + The client whose bundle this is will recieve an email with instructions and default username/password. +
@@ -278,6 +283,53 @@
+ + +
@@ -321,6 +373,20 @@
+ +
+
+
+
+

Server Settings

+
Set or update various settings, such as smtp credentials, that the server uses
+
+ +
+
+
+
+