mirror of https://github.com/h44z/wg-portal.git
				
				
				
			wip: create/update/...
This commit is contained in:
		
							parent
							
								
									ea65e6b43c
								
							
						
					
					
						commit
						cc06019738
					
				|  | @ -34,10 +34,12 @@ | ||||||
| 
 | 
 | ||||||
|         <form method="post" enctype="multipart/form-data"> |         <form method="post" enctype="multipart/form-data"> | ||||||
|             <input type="hidden" name="uid" value="{{.Peer.UID}}"> |             <input type="hidden" name="uid" value="{{.Peer.UID}}"> | ||||||
|  |             <input type="hidden" name="privkey" value="{{.Peer.PrivateKey}}"> | ||||||
|  |             <input type="hidden" name="presharedkey" value="{{.Peer.PresharedKey}}"> | ||||||
|             <div class="form-row"> |             <div class="form-row"> | ||||||
|                 <div class="form-group col-md-12"> |                 <div class="form-group col-md-12"> | ||||||
|                     <label for="inputServerPublicKey">Public Key</label> |                     <label for="inputServerPublicKey">Public Key</label> | ||||||
|                     <input type="text" name="pkey" disabled class="form-control" id="inputServerPublicKey" value="{{.Peer.PublicKey}}"> |                     <input type="text" name="pubkey" readonly class="form-control" id="inputServerPublicKey" value="{{.Peer.PublicKey}}"> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|             <div class="form-row"> |             <div class="form-row"> | ||||||
|  |  | ||||||
|  | @ -30,11 +30,12 @@ | ||||||
| 
 | 
 | ||||||
|         <form method="post" enctype="multipart/form-data"> |         <form method="post" enctype="multipart/form-data"> | ||||||
|             <input type="hidden" name="device" value="{{.Device.DeviceName}}"> |             <input type="hidden" name="device" value="{{.Device.DeviceName}}"> | ||||||
|  |             <input type="hidden" name="privkey" value="{{.Device.PrivateKey}}"> | ||||||
|             <h3>Server's interface configuration</h3> |             <h3>Server's interface configuration</h3> | ||||||
|             <div class="form-row"> |             <div class="form-row"> | ||||||
|                 <div class="form-group col-md-12"> |                 <div class="form-group col-md-12"> | ||||||
|                     <label for="inputServerPublicKey">Public Key</label> |                     <label for="inputServerPublicKey">Public Key</label> | ||||||
|                     <input type="text" name="pubkey" disabled class="form-control" id="inputServerPublicKey" value="{{.Device.PublicKey}}"> |                     <input type="text" name="pubkey" readonly class="form-control" id="inputServerPublicKey" value="{{.Device.PublicKey}}"> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|             <div class="form-row"> |             <div class="form-row"> | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
| <body id="page-top"> | <body id="page-top"> | ||||||
|     {{template "prt_nav.html" .}} |     {{template "prt_nav.html" .}} | ||||||
|     <div class="container"> |     <div class="container"> | ||||||
|         <h1>WireGuard VPN Administration</h1> |         <h1 class="mt-2">WireGuard VPN Administration</h1> | ||||||
| 
 | 
 | ||||||
|         <div class="card"> |         <div class="card"> | ||||||
|             <div class="card-header"> |             <div class="card-header"> | ||||||
|  | @ -77,9 +77,16 @@ | ||||||
| 
 | 
 | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|         <div></div> |         <div class="mt-4 row"> | ||||||
|  |             <div class="col-sm-10 col-12"> | ||||||
|                 <h2>Current VPN Users</h2> |                 <h2>Current VPN Users</h2> | ||||||
|         <div class="table-responsive"> |             </div> | ||||||
|  |             <div class="col-sm-2 col-12"> | ||||||
|  |                 <a href="/admin/peer/create" title="Add a LDAP user" class="btn btn-primary pull-right"><i class="fa fa-fw fa-user-plus"></i></a> | ||||||
|  |                 <a href="/admin/peer/create" title="Manual add a user" class="btn btn-primary pull-right"><i class="fa fa-fw fa-plus"></i>M</a> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="mt-2 table-responsive"> | ||||||
|             <table class="table table-sm" id="userTable"> |             <table class="table table-sm" id="userTable"> | ||||||
|                 <thead> |                 <thead> | ||||||
|                 <tr> |                 <tr> | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										1
									
								
								go.mod
								
								
								
								
							|  | @ -7,6 +7,7 @@ require ( | ||||||
| 	github.com/gin-gonic/contrib v0.0.0-20201005132743-ca038bbf2944 | 	github.com/gin-gonic/contrib v0.0.0-20201005132743-ca038bbf2944 | ||||||
| 	github.com/gin-gonic/gin v1.6.3 | 	github.com/gin-gonic/gin v1.6.3 | ||||||
| 	github.com/go-ldap/ldap/v3 v3.2.4 | 	github.com/go-ldap/ldap/v3 v3.2.4 | ||||||
|  | 	github.com/go-playground/validator/v10 v10.2.0 | ||||||
| 	github.com/gorilla/sessions v1.2.1 // indirect | 	github.com/gorilla/sessions v1.2.1 // indirect | ||||||
| 	github.com/kelseyhightower/envconfig v1.4.0 | 	github.com/kelseyhightower/envconfig v1.4.0 | ||||||
| 	github.com/sirupsen/logrus v1.7.0 | 	github.com/sirupsen/logrus v1.7.0 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,9 @@ | ||||||
| package common | package common | ||||||
| 
 | 
 | ||||||
| import "net" | import ( | ||||||
|  | 	"net" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| // BroadcastAddr returns the last address in the given network, or the broadcast address.
 | // BroadcastAddr returns the last address in the given network, or the broadcast address.
 | ||||||
| func BroadcastAddr(n *net.IPNet) net.IP { | func BroadcastAddr(n *net.IPNet) net.IP { | ||||||
|  | @ -35,3 +38,20 @@ func IsIPv6(address string) bool { | ||||||
| 	} | 	} | ||||||
| 	return ip.To4() == nil | 	return ip.To4() == nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func ParseIPList(lst string) []string { | ||||||
|  | 	ips := strings.Split(lst, ",") | ||||||
|  | 	validatedIPs := make([]string, 0, len(ips)) | ||||||
|  | 	for i := range ips { | ||||||
|  | 		ips[i] = strings.TrimSpace(ips[i]) | ||||||
|  | 		if ips[i] != "" { | ||||||
|  | 			validatedIPs = append(validatedIPs, ips[i]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return validatedIPs | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func IPListToString(lst []string) string { | ||||||
|  | 	return strings.Join(lst, ", ") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -38,6 +38,7 @@ type SessionData struct { | ||||||
| 	Search        string | 	Search        string | ||||||
| 	AlertData     string | 	AlertData     string | ||||||
| 	AlertType     string | 	AlertType     string | ||||||
|  | 	FormData      interface{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type AlertData struct { | type AlertData struct { | ||||||
|  |  | ||||||
|  | @ -1,12 +1,15 @@ | ||||||
| package server | package server | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/md5" | ||||||
|  | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" |  | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/h44z/wg-portal/internal/common" | ||||||
|  | 
 | ||||||
| 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes" | 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | @ -77,72 +80,22 @@ func (s *Server) GetAdminEditInterface(c *gin.Context) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Server) PostAdminEditInterface(c *gin.Context) { | func (s *Server) PostAdminEditInterface(c *gin.Context) { | ||||||
| 	device := s.users.GetDevice() | 	var formDevice Device | ||||||
| 	var err error | 	if err := c.ShouldBind(&formDevice); err != nil { | ||||||
| 
 | 		s.setAlert(c, "failed to bind form data: "+err.Error(), "danger") | ||||||
| 	device.ListenPort, err = strconv.Atoi(c.PostForm("port")) |  | ||||||
| 	if err != nil { |  | ||||||
| 		s.setAlert(c, "invalid port: "+err.Error(), "danger") |  | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ipField := c.PostForm("ip") |  | ||||||
| 	ips := strings.Split(ipField, ",") |  | ||||||
| 	validatedIPs := make([]string, 0, len(ips)) |  | ||||||
| 	for i := range ips { |  | ||||||
| 		ips[i] = strings.TrimSpace(ips[i]) |  | ||||||
| 		if ips[i] != "" { |  | ||||||
| 			validatedIPs = append(validatedIPs, ips[i]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if len(validatedIPs) == 0 { |  | ||||||
| 		s.setAlert(c, "invalid ip address", "danger") |  | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	device.IPs = validatedIPs |  | ||||||
| 
 |  | ||||||
| 	device.Endpoint = c.PostForm("endpoint") |  | ||||||
| 
 |  | ||||||
| 	dnsField := c.PostForm("dns") |  | ||||||
| 	dns := strings.Split(dnsField, ",") |  | ||||||
| 	validatedDNS := make([]string, 0, len(dns)) |  | ||||||
| 	for i := range dns { |  | ||||||
| 		dns[i] = strings.TrimSpace(dns[i]) |  | ||||||
| 		if dns[i] != "" { |  | ||||||
| 			validatedDNS = append(validatedDNS, dns[i]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	device.DNS = validatedDNS |  | ||||||
| 
 |  | ||||||
| 	allowedIPField := c.PostForm("allowedip") |  | ||||||
| 	allowedIP := strings.Split(allowedIPField, ",") |  | ||||||
| 	validatedAllowedIP := make([]string, 0, len(allowedIP)) |  | ||||||
| 	for i := range allowedIP { |  | ||||||
| 		allowedIP[i] = strings.TrimSpace(allowedIP[i]) |  | ||||||
| 		if allowedIP[i] != "" { |  | ||||||
| 			validatedAllowedIP = append(validatedAllowedIP, allowedIP[i]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	device.AllowedIPs = validatedAllowedIP |  | ||||||
| 
 |  | ||||||
| 	device.Mtu, err = strconv.Atoi(c.PostForm("mtu")) |  | ||||||
| 	if err != nil { |  | ||||||
| 		s.setAlert(c, "invalid MTU: "+err.Error(), "danger") |  | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	device.PersistentKeepalive, err = strconv.Atoi(c.PostForm("keepalive")) |  | ||||||
| 	if err != nil { |  | ||||||
| 		s.setAlert(c, "invalid PersistentKeepalive: "+err.Error(), "danger") |  | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") | 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	// Clean list input
 | ||||||
|  | 	formDevice.IPs = common.ParseIPList(formDevice.IPsStr) | ||||||
|  | 	formDevice.AllowedIPs = common.ParseIPList(formDevice.AllowedIPsStr) | ||||||
|  | 	formDevice.DNS = common.ParseIPList(formDevice.DNSStr) | ||||||
|  | 	formDevice.IPsStr = common.IPListToString(formDevice.IPs) | ||||||
|  | 	formDevice.AllowedIPsStr = common.IPListToString(formDevice.AllowedIPs) | ||||||
|  | 	formDevice.DNSStr = common.IPListToString(formDevice.DNS) | ||||||
| 
 | 
 | ||||||
| 	// Update WireGuard device
 | 	// Update WireGuard device
 | ||||||
| 	err = s.wg.UpdateDevice(device.DeviceName, device.GetDeviceConfig()) | 	err := s.wg.UpdateDevice(formDevice.DeviceName, formDevice.GetDeviceConfig()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.setAlert(c, "failed to update device in WireGuard: "+err.Error(), "danger") | 		s.setAlert(c, "failed to update device in WireGuard: "+err.Error(), "danger") | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") | 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") | ||||||
|  | @ -150,7 +103,7 @@ func (s *Server) PostAdminEditInterface(c *gin.Context) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Update in database
 | 	// Update in database
 | ||||||
| 	err = s.users.UpdateDevice(device) | 	err = s.users.UpdateDevice(formDevice) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.setAlert(c, "failed to update device in database: "+err.Error(), "danger") | 		s.setAlert(c, "failed to update device in database: "+err.Error(), "danger") | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") | 		c.Redirect(http.StatusSeeOther, "/admin/device/edit") | ||||||
|  | @ -183,77 +136,47 @@ func (s *Server) GetAdminEditPeer(c *gin.Context) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Server) PostAdminEditPeer(c *gin.Context) { | func (s *Server) PostAdminEditPeer(c *gin.Context) { | ||||||
| 	user := s.users.GetUserByKey(c.Query("pkey")) | 	currentUser := s.users.GetUserByKey(c.Query("pkey")) | ||||||
| 	urlEncodedKey := url.QueryEscape(c.Query("pkey")) | 	urlEncodedKey := url.QueryEscape(c.Query("pkey")) | ||||||
| 	var err error |  | ||||||
| 
 | 
 | ||||||
| 	user.Identifier = c.PostForm("identifier") | 	var formUser User | ||||||
| 	if user.Identifier == "" { | 	if err := c.ShouldBind(&formUser); err != nil { | ||||||
| 		s.setAlert(c, "invalid identifier, must not be empty", "danger") | 		s.setAlert(c, "failed to bind form data: "+err.Error(), "danger") | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | 		c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	user.Email = c.PostForm("mail") | 	// Clean list input
 | ||||||
| 	if user.Email == "" { | 	formUser.IPs = common.ParseIPList(formUser.IPsStr) | ||||||
| 		s.setAlert(c, "invalid email, must not be empty", "danger") | 	formUser.AllowedIPs = common.ParseIPList(formUser.AllowedIPsStr) | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | 	formUser.IPsStr = common.IPListToString(formUser.IPs) | ||||||
| 		return | 	formUser.AllowedIPsStr = common.IPListToString(formUser.AllowedIPs) | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	ipField := c.PostForm("ip") |  | ||||||
| 	ips := strings.Split(ipField, ",") |  | ||||||
| 	validatedIPs := make([]string, 0, len(ips)) |  | ||||||
| 	for i := range ips { |  | ||||||
| 		ips[i] = strings.TrimSpace(ips[i]) |  | ||||||
| 		if ips[i] != "" { |  | ||||||
| 			validatedIPs = append(validatedIPs, ips[i]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if len(validatedIPs) == 0 { |  | ||||||
| 		s.setAlert(c, "invalid ip address", "danger") |  | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	user.IPs = validatedIPs |  | ||||||
| 
 |  | ||||||
| 	allowedIPField := c.PostForm("allowedip") |  | ||||||
| 	allowedIP := strings.Split(allowedIPField, ",") |  | ||||||
| 	validatedAllowedIP := make([]string, 0, len(allowedIP)) |  | ||||||
| 	for i := range allowedIP { |  | ||||||
| 		allowedIP[i] = strings.TrimSpace(allowedIP[i]) |  | ||||||
| 		if allowedIP[i] != "" { |  | ||||||
| 			validatedAllowedIP = append(validatedAllowedIP, allowedIP[i]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	user.AllowedIPs = validatedAllowedIP |  | ||||||
| 
 |  | ||||||
| 	user.IgnorePersistentKeepalive = c.PostForm("ignorekeepalive") != "" |  | ||||||
| 	disabled := c.PostForm("isdisabled") != "" | 	disabled := c.PostForm("isdisabled") != "" | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| 	if disabled && user.DeactivatedAt == nil { | 	if disabled && currentUser.DeactivatedAt == nil { | ||||||
| 		user.DeactivatedAt = &now | 		formUser.DeactivatedAt = &now | ||||||
| 	} else if !disabled { | 	} else if !disabled { | ||||||
| 		user.DeactivatedAt = nil | 		formUser.DeactivatedAt = nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Update WireGuard device
 | 	// Update WireGuard device
 | ||||||
| 	if user.DeactivatedAt == &now { | 	if formUser.DeactivatedAt == &now { | ||||||
| 		err = s.wg.RemovePeer(user.PublicKey) | 		err := s.wg.RemovePeer(formUser.PublicKey) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			s.setAlert(c, "failed to remove peer in WireGuard: "+err.Error(), "danger") | 			s.setAlert(c, "failed to remove peer in WireGuard: "+err.Error(), "danger") | ||||||
| 			c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | 			c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} else if user.DeactivatedAt == nil && user.Peer != nil { | 	} else if formUser.DeactivatedAt == nil && currentUser.Peer != nil { | ||||||
| 		err = s.wg.UpdatePeer(user.GetPeerConfig()) | 		err := s.wg.UpdatePeer(formUser.GetPeerConfig()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			s.setAlert(c, "failed to update peer in WireGuard: "+err.Error(), "danger") | 			s.setAlert(c, "failed to update peer in WireGuard: "+err.Error(), "danger") | ||||||
| 			c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | 			c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} else if user.DeactivatedAt == nil && user.Peer == nil { | 	} else if formUser.DeactivatedAt == nil && currentUser.Peer == nil { | ||||||
| 		err = s.wg.AddPeer(user.GetPeerConfig()) | 		err := s.wg.AddPeer(formUser.GetPeerConfig()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			s.setAlert(c, "failed to add peer in WireGuard: "+err.Error(), "danger") | 			s.setAlert(c, "failed to add peer in WireGuard: "+err.Error(), "danger") | ||||||
| 			c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | 			c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | ||||||
|  | @ -262,7 +185,7 @@ func (s *Server) PostAdminEditPeer(c *gin.Context) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Update in database
 | 	// Update in database
 | ||||||
| 	err = s.users.UpdateUser(user) | 	err := s.users.UpdateUser(formUser) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.setAlert(c, "failed to update user in database: "+err.Error(), "danger") | 		s.setAlert(c, "failed to update user in database: "+err.Error(), "danger") | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | 		c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey) | ||||||
|  | @ -278,6 +201,20 @@ func (s *Server) GetAdminCreatePeer(c *gin.Context) { | ||||||
| 	user := User{} | 	user := User{} | ||||||
| 	user.AllowedIPsStr = device.AllowedIPsStr | 	user.AllowedIPsStr = device.AllowedIPsStr | ||||||
| 	user.IPsStr = "" // TODO: add a valid ip here
 | 	user.IPsStr = "" // TODO: add a valid ip here
 | ||||||
|  | 	psk, err := wgtypes.GenerateKey() | ||||||
|  | 	if err != nil { | ||||||
|  | 		s.HandleError(c, http.StatusInternalServerError, "Preshared key generation error", err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	key, err := wgtypes.GeneratePrivateKey() | ||||||
|  | 	if err != nil { | ||||||
|  | 		s.HandleError(c, http.StatusInternalServerError, "Private key generation error", err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	user.PresharedKey = psk.String() | ||||||
|  | 	user.PrivateKey = key.String() | ||||||
|  | 	user.PublicKey = key.PublicKey().String() | ||||||
|  | 	user.UID = fmt.Sprintf("u%x", md5.Sum([]byte(user.PublicKey))) | ||||||
| 
 | 
 | ||||||
| 	c.HTML(http.StatusOK, "admin_edit_client.html", struct { | 	c.HTML(http.StatusOK, "admin_edit_client.html", struct { | ||||||
| 		Route   string | 		Route   string | ||||||
|  | @ -297,68 +234,28 @@ func (s *Server) GetAdminCreatePeer(c *gin.Context) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Server) PostAdminCreatePeer(c *gin.Context) { | func (s *Server) PostAdminCreatePeer(c *gin.Context) { | ||||||
| 	user := User{} | 	var formUser User | ||||||
| 	key, err := wgtypes.GeneratePrivateKey() | 	if err := c.ShouldBind(&formUser); err != nil { | ||||||
| 	if err != nil { | 		s.setAlert(c, "failed to bind form data: "+err.Error(), "danger") | ||||||
| 		s.HandleError(c, http.StatusInternalServerError, "Private key generation error", err.Error()) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	user.PrivateKey = key.String() |  | ||||||
| 	user.PublicKey = key.PublicKey().String() |  | ||||||
| 
 |  | ||||||
| 	user.Identifier = c.PostForm("identifier") |  | ||||||
| 	if user.Identifier == "" { |  | ||||||
| 		s.setAlert(c, "invalid identifier, must not be empty", "danger") |  | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/peer/create") | 		c.Redirect(http.StatusSeeOther, "/admin/peer/create") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	user.Email = c.PostForm("mail") | 	// Clean list input
 | ||||||
| 	if user.Email == "" { | 	formUser.IPs = common.ParseIPList(formUser.IPsStr) | ||||||
| 		s.setAlert(c, "invalid email, must not be empty", "danger") | 	formUser.AllowedIPs = common.ParseIPList(formUser.AllowedIPsStr) | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/peer/create") | 	formUser.IPsStr = common.IPListToString(formUser.IPs) | ||||||
| 		return | 	formUser.AllowedIPsStr = common.IPListToString(formUser.AllowedIPs) | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	ipField := c.PostForm("ip") |  | ||||||
| 	ips := strings.Split(ipField, ",") |  | ||||||
| 	validatedIPs := make([]string, 0, len(ips)) |  | ||||||
| 	for i := range ips { |  | ||||||
| 		ips[i] = strings.TrimSpace(ips[i]) |  | ||||||
| 		if ips[i] != "" { |  | ||||||
| 			validatedIPs = append(validatedIPs, ips[i]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if len(validatedIPs) == 0 { |  | ||||||
| 		s.setAlert(c, "invalid ip address", "danger") |  | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/peer/create") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	user.IPs = validatedIPs |  | ||||||
| 
 |  | ||||||
| 	allowedIPField := c.PostForm("allowedip") |  | ||||||
| 	allowedIP := strings.Split(allowedIPField, ",") |  | ||||||
| 	validatedAllowedIP := make([]string, 0, len(allowedIP)) |  | ||||||
| 	for i := range allowedIP { |  | ||||||
| 		allowedIP[i] = strings.TrimSpace(allowedIP[i]) |  | ||||||
| 		if allowedIP[i] != "" { |  | ||||||
| 			validatedAllowedIP = append(validatedAllowedIP, allowedIP[i]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	user.AllowedIPs = validatedAllowedIP |  | ||||||
| 
 |  | ||||||
| 	user.IgnorePersistentKeepalive = c.PostForm("ignorekeepalive") != "" |  | ||||||
| 	disabled := c.PostForm("isdisabled") != "" | 	disabled := c.PostForm("isdisabled") != "" | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| 	if disabled && user.DeactivatedAt == nil { | 	if disabled { | ||||||
| 		user.DeactivatedAt = &now | 		formUser.DeactivatedAt = &now | ||||||
| 	} else if !disabled { |  | ||||||
| 		user.DeactivatedAt = nil |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Update WireGuard device
 | 	// Update WireGuard device
 | ||||||
| 	if user.DeactivatedAt == nil { | 	if formUser.DeactivatedAt == nil { | ||||||
| 		err = s.wg.AddPeer(user.GetPeerConfig()) | 		err := s.wg.AddPeer(formUser.GetPeerConfig()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			s.setAlert(c, "failed to add peer in WireGuard: "+err.Error(), "danger") | 			s.setAlert(c, "failed to add peer in WireGuard: "+err.Error(), "danger") | ||||||
| 			c.Redirect(http.StatusSeeOther, "/admin/peer/create") | 			c.Redirect(http.StatusSeeOther, "/admin/peer/create") | ||||||
|  | @ -367,7 +264,7 @@ func (s *Server) PostAdminCreatePeer(c *gin.Context) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Update in database
 | 	// Update in database
 | ||||||
| 	err = s.users.CreateUser(user) | 	err := s.users.CreateUser(formUser) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.setAlert(c, "failed to add user in database: "+err.Error(), "danger") | 		s.setAlert(c, "failed to add user in database: "+err.Error(), "danger") | ||||||
| 		c.Redirect(http.StatusSeeOther, "/admin/peer/create") | 		c.Redirect(http.StatusSeeOther, "/admin/peer/create") | ||||||
|  |  | ||||||
|  | @ -10,6 +10,10 @@ import ( | ||||||
| 	"text/template" | 	"text/template" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/gin-gonic/gin/binding" | ||||||
|  | 
 | ||||||
|  | 	"github.com/go-playground/validator/v10" | ||||||
|  | 
 | ||||||
| 	"github.com/h44z/wg-portal/internal/wireguard" | 	"github.com/h44z/wg-portal/internal/wireguard" | ||||||
| 
 | 
 | ||||||
| 	"github.com/h44z/wg-portal/internal/common" | 	"github.com/h44z/wg-portal/internal/common" | ||||||
|  | @ -22,6 +26,42 @@ import ( | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | //
 | ||||||
|  | // CUSTOM VALIDATORS ----------------------------------------------------------------------------
 | ||||||
|  | //
 | ||||||
|  | var cidrList validator.Func = func(fl validator.FieldLevel) bool { | ||||||
|  | 	cidrListStr := fl.Field().String() | ||||||
|  | 
 | ||||||
|  | 	cidrList := common.ParseIPList(cidrListStr) | ||||||
|  | 	for i := range cidrList { | ||||||
|  | 		_, _, err := net.ParseCIDR(cidrList[i]) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var ipList validator.Func = func(fl validator.FieldLevel) bool { | ||||||
|  | 	ipListStr := fl.Field().String() | ||||||
|  | 
 | ||||||
|  | 	ipList := common.ParseIPList(ipListStr) | ||||||
|  | 	for i := range ipList { | ||||||
|  | 		ip := net.ParseIP(ipList[i]) | ||||||
|  | 		if ip == nil { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	if v, ok := binding.Validator.Engine().(*validator.Validate); ok { | ||||||
|  | 		v.RegisterValidation("cidrlist", cidrList) | ||||||
|  | 		v.RegisterValidation("iplist", ipList) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| //
 | //
 | ||||||
| //  USER ----------------------------------------------------------------------------------------
 | //  USER ----------------------------------------------------------------------------------------
 | ||||||
| //
 | //
 | ||||||
|  | @ -31,19 +71,19 @@ type User struct { | ||||||
| 	LdapUser *ldap.UserCacheHolderEntry `gorm:"-"` // optional, it is still possible to have users without ldap
 | 	LdapUser *ldap.UserCacheHolderEntry `gorm:"-"` // optional, it is still possible to have users without ldap
 | ||||||
| 	Config   string                     `gorm:"-"` | 	Config   string                     `gorm:"-"` | ||||||
| 
 | 
 | ||||||
| 	UID        string // uid for html identification
 | 	UID        string `form:"uid" binding:"alphanum"` // uid for html identification
 | ||||||
| 	IsOnline   bool   `gorm:"-"` | 	IsOnline   bool   `gorm:"-"` | ||||||
| 	Identifier string // Identifier AND Email make a WireGuard peer unique
 | 	Identifier string `form:"identifier" binding:"required,lt=64"` // Identifier AND Email make a WireGuard peer unique
 | ||||||
| 	Email      string `gorm:"index"` | 	Email      string `gorm:"index" form:"mail" binding:"required,email"` | ||||||
| 
 | 
 | ||||||
| 	IgnorePersistentKeepalive bool | 	IgnorePersistentKeepalive bool     `form:"ignorekeepalive"` | ||||||
| 	PresharedKey              string | 	PresharedKey              string   `form:"presharedkey" binding:"omitempty,base64"` | ||||||
| 	AllowedIPsStr             string | 	AllowedIPsStr             string   `form:"allowedip" binding:"cidrlist"` | ||||||
| 	IPsStr                    string | 	IPsStr                    string   `form:"ip" binding:"cidrlist"` | ||||||
| 	AllowedIPs                []string `gorm:"-"` // IPs that are used in the client config file
 | 	AllowedIPs                []string `gorm:"-"` // IPs that are used in the client config file
 | ||||||
| 	IPs                       []string `gorm:"-"` // The IPs of the client
 | 	IPs                       []string `gorm:"-"` // The IPs of the client
 | ||||||
| 	PrivateKey                string | 	PrivateKey                string   `form:"privkey" binding:"omitempty,base64"` | ||||||
| 	PublicKey                 string `gorm:"primaryKey"` | 	PublicKey                 string   `gorm:"primaryKey" form:"pubkey" binding:"required,base64"` | ||||||
| 
 | 
 | ||||||
| 	DeactivatedAt *time.Time | 	DeactivatedAt *time.Time | ||||||
| 	CreatedBy     string | 	CreatedBy     string | ||||||
|  | @ -128,23 +168,23 @@ func (u User) IsValid() bool { | ||||||
| type Device struct { | type Device struct { | ||||||
| 	Interface *wgtypes.Device `gorm:"-"` | 	Interface *wgtypes.Device `gorm:"-"` | ||||||
| 
 | 
 | ||||||
| 	DeviceName          string `gorm:"primaryKey"` | 	DeviceName          string   `form:"device" gorm:"primaryKey" binding:"required,alphanum"` | ||||||
| 	PrivateKey          string | 	PrivateKey          string   `form:"privkey" binding:"base64"` | ||||||
| 	PublicKey           string | 	PublicKey           string   `form:"pubkey" binding:"required,base64"` | ||||||
| 	PersistentKeepalive int | 	PersistentKeepalive int      `form:"keepalive" binding:"gte=0"` | ||||||
| 	ListenPort          int | 	ListenPort          int      `form:"port" binding:"required,gt=0"` | ||||||
| 	Mtu                 int | 	Mtu                 int      `form:"mtu" binding:"gte=0,lte=1500"` | ||||||
| 	Endpoint            string | 	Endpoint            string   `form:"endpoint" binding:"required,hostname_port"` | ||||||
| 	AllowedIPsStr       string | 	AllowedIPsStr       string   `form:"allowedip" binding:"cidrlist"` | ||||||
| 	IPsStr              string | 	IPsStr              string   `form:"ip" binding:"required,cidrlist"` | ||||||
| 	AllowedIPs          []string `gorm:"-"` // IPs that are used in the client config file
 | 	AllowedIPs          []string `gorm:"-"` // IPs that are used in the client config file
 | ||||||
| 	IPs                 []string `gorm:"-"` // The IPs of the client
 | 	IPs                 []string `gorm:"-"` // The IPs of the client
 | ||||||
| 	DNSStr              string | 	DNSStr              string   `form:"dns" binding:"iplist"` | ||||||
| 	DNS                 []string `gorm:"-"` // The DNS servers of the client
 | 	DNS                 []string `gorm:"-"` // The DNS servers of the client
 | ||||||
| 	PreUp               string | 	PreUp               string   `form:"preup"` | ||||||
| 	PostUp              string | 	PostUp              string   `form:"postup"` | ||||||
| 	PreDown             string | 	PreDown             string   `form:"predown"` | ||||||
| 	PostDown            string | 	PostDown            string   `form:"postdown"` | ||||||
| 	CreatedAt           time.Time | 	CreatedAt           time.Time | ||||||
| 	UpdatedAt           time.Time | 	UpdatedAt           time.Time | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue