diff --git a/assets/tpl/user_create_client.html b/assets/tpl/user_create_client.html new file mode 100644 index 00000000..5e6540c3 --- /dev/null +++ b/assets/tpl/user_create_client.html @@ -0,0 +1,44 @@ + + + + + + {{ .Static.WebsiteTitle }} - Admin + + + + + + + + {{template "prt_nav.html" .}} +
+ {{template "prt_flashes.html" .}} + + +

Create a new client

+ +
+ + +
+
+ + +
+
+ + + Cancel +
+
+ {{template "prt_footer.html" .}} + + + + + + + + + \ No newline at end of file diff --git a/assets/tpl/user_edit_client.html b/assets/tpl/user_edit_client.html new file mode 100644 index 00000000..9d731746 --- /dev/null +++ b/assets/tpl/user_edit_client.html @@ -0,0 +1,54 @@ + + + + + + {{ .Static.WebsiteTitle }} - Admin + + + + + + + + {{template "prt_nav.html" .}} +
+ {{template "prt_flashes.html" .}} + + +

Edit client: {{.Peer.Identifier}}

+ +
+ + +
+
+ + +
+
+ +
+
+
+ + +
+
+
+ + Cancel +
+
+ {{template "prt_footer.html" .}} + + + + + + + + + \ No newline at end of file diff --git a/assets/tpl/user_index.html b/assets/tpl/user_index.html index 1694af7d..f0e53d78 100644 --- a/assets/tpl/user_index.html +++ b/assets/tpl/user_index.html @@ -15,7 +15,16 @@

WireGuard VPN User-Portal

-

Your VPN Profiles

+
+
+

Your VPN Profiles

+
+
+ {{if eq $.UserManagePeers true}} + + {{end}} +
+
@@ -26,6 +35,9 @@

Your VPN Profiles

+ {{if eq $.UserManagePeers true}} + + {{end}} @@ -42,6 +54,11 @@

Your VPN Profiles

+ {{if eq $.UserManagePeers true}} + + {{end}}
E-Mail IP's Handshake
{{$p.Email}} {{$p.IPsStr}} {{$p.LastHandshake}} + +
diff --git a/internal/server/configuration.go b/internal/server/configuration.go index 3b013d1c..f904dbb5 100644 --- a/internal/server/configuration.go +++ b/internal/server/configuration.go @@ -116,6 +116,7 @@ func NewConfig() *Config { cfg.WG.DefaultDeviceName = "wg0" cfg.WG.ConfigDirectoryPath = "/etc/wireguard" cfg.WG.ManageIPAddresses = true + cfg.WG.UserManagePeers = false cfg.Email.Host = "127.0.0.1" cfg.Email.Port = 25 cfg.Email.Encryption = common.MailEncryptionNone diff --git a/internal/server/handlers_common.go b/internal/server/handlers_common.go index ab851bbd..6a30812f 100644 --- a/internal/server/handlers_common.go +++ b/internal/server/handlers_common.go @@ -135,15 +135,16 @@ func (s *Server) GetUserIndex(c *gin.Context) { peers := s.peers.GetSortedPeersForEmail(currentSession.SortedBy["userpeers"], currentSession.SortDirection["userpeers"], currentSession.Email) c.HTML(http.StatusOK, "user_index.html", gin.H{ - "Route": c.Request.URL.Path, - "Alerts": GetFlashes(c), - "Session": currentSession, - "Static": s.getStaticData(), - "Peers": peers, - "TotalPeers": len(peers), - "Users": []users.User{*s.users.GetUser(currentSession.Email)}, - "Device": s.peers.GetDevice(currentSession.DeviceName), - "DeviceNames": s.GetDeviceNames(), + "Route": c.Request.URL.Path, + "Alerts": GetFlashes(c), + "Session": currentSession, + "Static": s.getStaticData(), + "Peers": peers, + "TotalPeers": len(peers), + "Users": []users.User{*s.users.GetUser(currentSession.Email)}, + "Device": s.peers.GetDevice(currentSession.DeviceName), + "DeviceNames": s.GetDeviceNames(), + "UserManagePeers": s.config.WG.UserManagePeers, }) } diff --git a/internal/server/handlers_peer.go b/internal/server/handlers_peer.go index 36664091..76aa376e 100644 --- a/internal/server/handlers_peer.go +++ b/internal/server/handlers_peer.go @@ -392,3 +392,117 @@ func (s *Server) sendPeerConfigMail(peer wireguard.Peer) error { return nil } + +func (s *Server) GetUserCreatePeer(c *gin.Context) { + currentSession, err := s.setNewPeerFormInSession(c) + if err != nil { + s.GetHandleError(c, http.StatusInternalServerError, "Session error", err.Error()) + return + } + c.HTML(http.StatusOK, "user_create_client.html", gin.H{ + "Route": c.Request.URL.Path, + "Alerts": GetFlashes(c), + "Session": currentSession, + "Static": s.getStaticData(), + "Peer": currentSession.FormData.(wireguard.Peer), + "EditableKeys": s.config.Core.EditableKeys, + "Device": s.peers.GetDevice(currentSession.DeviceName), + "DeviceNames": s.GetDeviceNames(), + "AdminEmail": s.config.Core.AdminUser, + "Csrf": csrf.GetToken(c), + }) +} + +func (s *Server) PostUserCreatePeer(c *gin.Context) { + currentSession := GetSessionData(c) + var formPeer wireguard.Peer + if currentSession.FormData != nil { + formPeer = currentSession.FormData.(wireguard.Peer) + } + + formPeer.Email = currentSession.Email; + formPeer.Identifier = currentSession.Email; + formPeer.DeviceType = wireguard.DeviceTypeServer; + formPeer.PrivateKey = ""; + + if err := c.ShouldBind(&formPeer); err != nil { + _ = s.updateFormInSession(c, formPeer) + SetFlashMessage(c, "failed to bind form data: "+err.Error(), "danger") + c.Redirect(http.StatusSeeOther, "/user/peer/create?formerr=bind") + return + } + + disabled := c.PostForm("isdisabled") != "" + now := time.Now() + if disabled { + formPeer.DeactivatedAt = &now + } + + if err := s.CreatePeer(currentSession.DeviceName, formPeer); err != nil { + _ = s.updateFormInSession(c, formPeer) + SetFlashMessage(c, "failed to add user: "+err.Error(), "danger") + c.Redirect(http.StatusSeeOther, "/user/peer/create?formerr=create") + return + } + + SetFlashMessage(c, "client created successfully", "success") + c.Redirect(http.StatusSeeOther, "/user/profile") +} + +func (s *Server) GetUserEditPeer(c *gin.Context) { + peer := s.peers.GetPeerByKey(c.Query("pkey")) + + + currentSession, err := s.setFormInSession(c, peer) + if err != nil { + s.GetHandleError(c, http.StatusInternalServerError, "Session error", err.Error()) + return + } + + if peer.Email != currentSession.Email { + s.GetHandleError(c, http.StatusUnauthorized, "No permissions", "You don't have permissions to view this resource!") + return; + } + + c.HTML(http.StatusOK, "user_edit_client.html", gin.H{ + "Route": c.Request.URL.Path, + "Alerts": GetFlashes(c), + "Session": currentSession, + "Static": s.getStaticData(), + "Peer": currentSession.FormData.(wireguard.Peer), + "EditableKeys": s.config.Core.EditableKeys, + "Device": s.peers.GetDevice(currentSession.DeviceName), + "DeviceNames": s.GetDeviceNames(), + "AdminEmail": s.config.Core.AdminUser, + "Csrf": csrf.GetToken(c), + }) +} + +func (s *Server) PostUserEditPeer(c *gin.Context) { + currentPeer := s.peers.GetPeerByKey(c.Query("pkey")) + urlEncodedKey := url.QueryEscape(c.Query("pkey")) + + currentSession := GetSessionData(c) + + if currentPeer.Email != currentSession.Email { + s.GetHandleError(c, http.StatusUnauthorized, "No permissions", "You don't have permissions to view this resource!") + return; + } + + disabled := c.PostForm("isdisabled") != "" + now := time.Now() + if disabled && currentPeer.DeactivatedAt == nil { + currentPeer.DeactivatedAt = &now + } + + // Update in database + if err := s.UpdatePeer(currentPeer, now); err != nil { + _ = s.updateFormInSession(c, currentPeer) + SetFlashMessage(c, "failed to update user: "+err.Error(), "danger") + c.Redirect(http.StatusSeeOther, "/user/peer/edit?pkey="+urlEncodedKey+"&formerr=update") + return + } + + SetFlashMessage(c, "changes applied successfully", "success") + c.Redirect(http.StatusSeeOther, "/user/peer/edit?pkey="+urlEncodedKey) +} \ No newline at end of file diff --git a/internal/server/routes.go b/internal/server/routes.go index 3922cad1..98660355 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -76,6 +76,13 @@ func SetupRoutes(s *Server) { user.GET("/download", s.GetPeerConfig) user.GET("/email", s.GetPeerConfigMail) user.GET("/status", s.GetPeerStatus) + + if s.config.WG.UserManagePeers { + user.GET("/peer/create", s.GetUserCreatePeer) + user.POST("/peer/create", s.PostUserCreatePeer) + user.GET("/peer/edit", s.GetUserEditPeer) + user.POST("/peer/edit", s.PostUserEditPeer) + } } func SetupApiRoutes(s *Server) { diff --git a/internal/wireguard/config.go b/internal/wireguard/config.go index 29370134..0d079e3e 100644 --- a/internal/wireguard/config.go +++ b/internal/wireguard/config.go @@ -7,6 +7,7 @@ type Config struct { DefaultDeviceName string `yaml:"defaultDevice" envconfig:"WG_DEFAULT_DEVICE"` // this device is used for auto-created peers, use GetDefaultDeviceName() to access this field ConfigDirectoryPath string `yaml:"configDirectory" envconfig:"WG_CONFIG_PATH"` // optional, if set, updates will be written to this path, filename: .conf ManageIPAddresses bool `yaml:"manageIPAddresses" envconfig:"MANAGE_IPS"` // handle ip-address setup of interface + UserManagePeers bool `yaml:"userManagePeers" envconfig:"USER_MANAGE_PEERS"` // user can manage own peers } func (c Config) GetDefaultDeviceName() string { diff --git a/internal/wireguard/tpl/peer.tpl b/internal/wireguard/tpl/peer.tpl index d6a85e40..e27b719f 100644 --- a/internal/wireguard/tpl/peer.tpl +++ b/internal/wireguard/tpl/peer.tpl @@ -5,7 +5,8 @@ [Interface] # Core settings -PrivateKey = {{ .Peer.PrivateKey }} + +PrivateKey = {{or .Peer.PrivateKey "" }} Address = {{ .Peer.IPsStr }} # Misc. settings (optional)