From 5fff577c603c10ac646c002134a41d27fcd6ab20 Mon Sep 17 00:00:00 2001
From: Arminas 
Date: Fri, 10 Feb 2023 07:22:24 +0200
Subject: [PATCH] Added client default settings page
Added client default settings page, where you can set Allowed IPs, Extra Allowed IPs, use server dns, enable after creation.
---
 handler/routes.go                      |  60 +++++++++
 main.go                                |   2 +
 model/client_defaults.go               |   8 +-
 router/router.go                       |   8 +-
 store/jsondb/jsondb.go                 |  19 ++-
 store/store.go                         |   2 +
 templates/base.html                    |  10 +-
 templates/client_default_settings.html | 173 +++++++++++++++++++++++++
 util/util.go                           |  18 +++
 9 files changed, 293 insertions(+), 7 deletions(-)
 create mode 100644 templates/client_default_settings.html
diff --git a/handler/routes.go b/handler/routes.go
index 3ddbb2d..c1c61d9 100644
--- a/handler/routes.go
+++ b/handler/routes.go
@@ -596,6 +596,22 @@ func GlobalSettings(db store.IStore) echo.HandlerFunc {
 	}
 }
 
+// ClientDefaultSettings handler
+func ClientDefaultSettings(db store.IStore) echo.HandlerFunc {
+	return func(c echo.Context) error {
+
+		clientDefaultSettings, err := db.GetClientDefaultSettings()
+		if err != nil {
+			log.Error("Cannot get client default settings: ", err)
+		}
+
+		return c.Render(http.StatusOK, "client_default_settings.html", map[string]interface{}{
+			"baseData":              model.BaseData{Active: "client-default-settings", CurrentUser: currentUser(c)},
+			"clientDefaultSettings": clientDefaultSettings,
+		})
+	}
+}
+
 // Status handler
 func Status(db store.IStore) echo.HandlerFunc {
 	type PeerVM struct {
@@ -709,6 +725,50 @@ func GlobalSettingSubmit(db store.IStore) echo.HandlerFunc {
 	}
 }
 
+// ClientDefaultSettingsSubmit handler to update the client default settings
+func ClientDefaultSettingsSubmit(db store.IStore) echo.HandlerFunc {
+	return func(c echo.Context) error {
+
+		//data := make(map[string]interface{})
+		//err := json.NewDecoder(c.Request().Body).Decode(&data)
+		//if err != nil {
+		//	return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Bad post data"})
+		//}
+		//
+		//var clientDefaultSettings model.ClientDefaults
+		//fmt.Println(data["allowed_ips"])
+		//
+		//clientDefaultSettings.AllowedIps = strings.Split(data["allowed_ips"].(string), ",")
+		//clientDefaultSettings.ExtraAllowedIps = strings.Split(data["extra_allowed_ips"].(string), ",")
+		//clientDefaultSettings.EnableAfterCreation = data["enable_after_creation"].(bool)
+		//clientDefaultSettings.UseServerDNS = data["use_server_dns"].(bool)
+
+		var clientDefaultSettings model.ClientDefaults
+		c.Bind(&clientDefaultSettings)
+
+		// validate the input allowed ips list
+		if util.ValidateCIDRList(clientDefaultSettings.AllowedIps, true) == false {
+			log.Warnf("Invalid Allowed IPs list input from user: %v", clientDefaultSettings.AllowedIps)
+			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Allowed IPs must be in CIDR format"})
+		}
+
+		// validate the input extra allowed ips list
+		if util.ValidateCIDRList(clientDefaultSettings.ExtraAllowedIps, true) == false {
+			log.Warnf("Invalid Extra Allowed IPs list input from user: %v", clientDefaultSettings.ExtraAllowedIps)
+			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Extra Allowed IPs must be in CIDR format"})
+		}
+
+		// write config to the database
+		if err := db.SaveClientDefaultSettings(clientDefaultSettings); err != nil {
+			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Error saving client default settings"})
+		}
+
+		log.Infof("Updated client default settings: %v", clientDefaultSettings)
+
+		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated client default settings successfully"})
+	}
+}
+
 // MachineIPAddresses handler to get local interface ip addresses
 func MachineIPAddresses() echo.HandlerFunc {
 	return func(c echo.Context) error {
diff --git a/main.go b/main.go
index 3f0cd13..edc24fe 100644
--- a/main.go
+++ b/main.go
@@ -158,7 +158,9 @@ func main() {
 	app.POST(util.BasePath+"/wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession, handler.ContentTypeJson)
 	app.POST(util.BasePath+"/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession, handler.ContentTypeJson)
 	app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession)
+	app.GET(util.BasePath+"/client-default-settings", handler.ClientDefaultSettings(db), handler.ValidSession)
 	app.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson)
+	app.POST(util.BasePath+"/client-default-settings", handler.ClientDefaultSettingsSubmit(db), handler.ValidSession, handler.ContentTypeJson)
 	app.GET(util.BasePath+"/status", handler.Status(db), handler.ValidSession)
 	app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession)
 	app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession)
diff --git a/model/client_defaults.go b/model/client_defaults.go
index 615ebed..b8cfb72 100644
--- a/model/client_defaults.go
+++ b/model/client_defaults.go
@@ -2,8 +2,8 @@ package model
 
 // ClientDefaults Defaults for creation of new clients used in the templates
 type ClientDefaults struct {
-	AllowedIps          []string
-	ExtraAllowedIps     []string
-	UseServerDNS        bool
-	EnableAfterCreation bool
+	AllowedIps          []string `json:"allowed_ips"`
+	ExtraAllowedIps     []string `json:"extra_allowed_ips"`
+	UseServerDNS        bool     `json:"use_server_dns"`
+	EnableAfterCreation bool     `json:"enable_after_creation"`
 }
diff --git a/router/router.go b/router/router.go
index 0f9facc..cdb2d22 100644
--- a/router/router.go
+++ b/router/router.go
@@ -36,7 +36,7 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c
 			data.(map[string]interface{})[k] = v
 		}
 
-		data.(map[string]interface{})["client_defaults"] = util.ClientDefaultsFromEnv()
+		data.(map[string]interface{})["client_defaults"] = util.ClientDefaultsFromDatabase()
 	}
 
 	// login page does not need the base layout
@@ -83,6 +83,11 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec
 		log.Fatal(err)
 	}
 
+	tmplClientDefaultSettingsString, err := tmplBox.String("client_default_settings.html")
+	if err != nil {
+		log.Fatal(err)
+	}
+
 	tmplStatusString, err := tmplBox.String("status.html")
 	if err != nil {
 		log.Fatal(err)
@@ -103,6 +108,7 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec
 	templates["clients.html"] = template.Must(template.New("clients").Funcs(funcs).Parse(tmplBaseString + tmplClientsString))
 	templates["server.html"] = template.Must(template.New("server").Funcs(funcs).Parse(tmplBaseString + tmplServerString))
 	templates["global_settings.html"] = template.Must(template.New("global_settings").Funcs(funcs).Parse(tmplBaseString + tmplGlobalSettingsString))
+	templates["client_default_settings.html"] = template.Must(template.New("client_default_settings").Funcs(funcs).Parse(tmplBaseString + tmplClientDefaultSettingsString))
 	templates["status.html"] = template.Must(template.New("status").Funcs(funcs).Parse(tmplBaseString + tmplStatusString))
 	templates["wake_on_lan_hosts.html"] = template.Must(template.New("wake_on_lan_hosts").Funcs(funcs).Parse(tmplBaseString + tmplWakeOnLanHostsString))
 
diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go
index f39a452..c29464e 100644
--- a/store/jsondb/jsondb.go
+++ b/store/jsondb/jsondb.go
@@ -42,6 +42,7 @@ func (o *JsonDB) Init() error {
 	var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
 	var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
 	var globalSettingPath string = path.Join(serverPath, "global_settings.json")
+	var clientDefaultSettingsPath string = path.Join(serverPath, "client_default_settings.json")
 	var userPath string = path.Join(serverPath, "users.json")
 	// create directories if they do not exist
 	if _, err := os.Stat(clientPath); os.IsNotExist(err) {
@@ -102,6 +103,12 @@ func (o *JsonDB) Init() error {
 		o.conn.Write("server", "global_settings", globalSetting)
 	}
 
+	// client default settings
+	if _, err := os.Stat(clientDefaultSettingsPath); os.IsNotExist(err) {
+		clientDefaultSetting := util.ClientDefaultsFromEnv()
+		o.conn.Write("server", "client_default_settings", clientDefaultSetting)
+	}
+
 	// user info
 	if _, err := os.Stat(userPath); os.IsNotExist(err) {
 		user := new(model.User)
@@ -138,6 +145,12 @@ func (o *JsonDB) GetGlobalSettings() (model.GlobalSetting, error) {
 	return settings, o.conn.Read("server", "global_settings", &settings)
 }
 
+// GetClientDefaultSettings func to query client default settings from the database
+func (o *JsonDB) GetClientDefaultSettings() (model.ClientDefaults, error) {
+	settings := model.ClientDefaults{}
+	return settings, o.conn.Read("server", "client_default_settings", &settings)
+}
+
 // GetServer func to query Server settings from the database
 func (o *JsonDB) GetServer() (model.Server, error) {
 	server := model.Server{}
@@ -213,7 +226,7 @@ func (o *JsonDB) GetClientByID(clientID string, qrCodeSettings model.QRCodeSetti
 		server, _ := o.GetServer()
 		globalSettings, _ := o.GetGlobalSettings()
 		client := client
-		if !qrCodeSettings.IncludeDNS{
+		if !qrCodeSettings.IncludeDNS {
 			globalSettings.DNSServers = []string{}
 		}
 		if !qrCodeSettings.IncludeMTU {
@@ -255,3 +268,7 @@ func (o *JsonDB) SaveServerKeyPair(serverKeyPair model.ServerKeypair) error {
 func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error {
 	return o.conn.Write("server", "global_settings", globalSettings)
 }
+
+func (o *JsonDB) SaveClientDefaultSettings(clientDefaults model.ClientDefaults) error {
+	return o.conn.Write("server", "client_default_settings", clientDefaults)
+}
diff --git a/store/store.go b/store/store.go
index 86d6224..d35fc59 100644
--- a/store/store.go
+++ b/store/store.go
@@ -9,6 +9,7 @@ type IStore interface {
 	GetUser() (model.User, error)
 	SaveUser(user model.User) error
 	GetGlobalSettings() (model.GlobalSetting, error)
+	GetClientDefaultSettings() (model.ClientDefaults, error)
 	GetServer() (model.Server, error)
 	GetClients(hasQRCode bool) ([]model.ClientData, error)
 	GetClientByID(clientID string, qrCode model.QRCodeSettings) (model.ClientData, error)
@@ -17,6 +18,7 @@ type IStore interface {
 	SaveServerInterface(serverInterface model.ServerInterface) error
 	SaveServerKeyPair(serverKeyPair model.ServerKeypair) error
 	SaveGlobalSettings(globalSettings model.GlobalSetting) error
+	SaveClientDefaultSettings(clientDefaults model.ClientDefaults) error
 	GetWakeOnLanHosts() ([]model.WakeOnLanHost, error)
 	GetWakeOnLanHost(macAddress string) (*model.WakeOnLanHost, error)
 	DeleteWakeOnHostLanHost(macAddress string) error
diff --git a/templates/base.html b/templates/base.html
index fd337a7..6f89679 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -124,6 +124,14 @@
                                 
                             
                         
+                        
+                            
+                            
+                            
+                                Client Default Settings
+                            
+                            
+                        
                         
                         
                             
@@ -488,7 +496,7 @@
                 $("#client_public_key").val("");
                 $("#client_preshared_key").val("");
                 $("#client_allocated_ips").importTags('');
-                $("#client_extra_allowed_ips").importTags('');
+                //$("#client_extra_allowed_ips").importTags('');
                 updateIPAllocationSuggestion();
             });
         });
diff --git a/templates/client_default_settings.html b/templates/client_default_settings.html
new file mode 100644
index 0000000..b949f04
--- /dev/null
+++ b/templates/client_default_settings.html
@@ -0,0 +1,173 @@
+{{define "title"}}
+Client Defaults Settings
+{{end}}
+
+{{define "top_css"}}
+{{end}}
+
+{{define "username"}}
+{{ .username }}
+{{end}}
+
+{{define "page_title"}}
+Client Defaults Settings
+{{end}}
+
+{{define "page_content"}}
+
+  
+    
+    
+      
+      
+      
+        
+          
+          
+          
+            
+              - 1. Endpoint Address+
- The public IP address of your Wireguard server that the client will connect to. Click on
+                Suggest button to auto detect the public IP address of your server.+
- 2. DNS Servers+
- The DNS servers will be set to client config.+
- 3. MTU+
- The MTU will be set to server and client config. By default it is 1420. You might want
+                to adjust the MTU size if your connection (e.g PPPoE, 3G, satellite network, etc) has a low MTU.
+- Leave blank to omit this setting in the configs.+
- 4. Persistent Keepalive+
- By default, WireGuard peers remain silent while they do not need to communicate,
+                so peers located behind a NAT and/or firewall may be unreachable from other peers
+                until they reach out to other peers themselves. Adding PersistentKeepalive+                can ensure that the connection remains open.
+- Leave blank to omit this setting in the Client config.+
- 5. Forward Mark+
- Set an fwmarkon all packets going out of WireGuard's UDP socket. Default value:0xca6c
+- 6. Wireguard Config File Path+
- The path of your Wireguard server config file. Please make sure the parent directory
+                exists and is writable.+
+          
+        
+        
+      
+    
+    
+