Skip to content

Commit a5fbb7b

Browse files
committed
Add authentication
1 parent a78c9f3 commit a5fbb7b

File tree

13 files changed

+306
-7
lines changed

13 files changed

+306
-7
lines changed

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ go 1.14
55
require (
66
github.com/glendc/go-external-ip v0.0.0-20170425150139-139229dcdddd
77
github.com/go-playground/universal-translator v0.17.0 // indirect
8+
github.com/gorilla/sessions v1.2.0
89
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 // indirect
10+
github.com/labstack/echo-contrib v0.9.0
911
github.com/labstack/echo/v4 v4.1.16
1012
github.com/labstack/gommon v0.3.0
1113
github.com/leodido/go-urn v1.2.0 // indirect

go.sum

+81
Large diffs are not rendered by default.

handler/routes.go

+89
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"net/http"
77
"time"
88

9+
"github.com/gorilla/sessions"
10+
"github.com/labstack/echo-contrib/session"
911
"github.com/labstack/echo/v4"
1012
"github.com/labstack/gommon/log"
1113
"github.com/ngoduykhanh/wireguard-ui/model"
@@ -21,16 +23,68 @@ func LoginPage() echo.HandlerFunc {
2123
}
2224
}
2325

26+
// Login for signing in handler
27+
func Login() echo.HandlerFunc {
28+
return func(c echo.Context) error {
29+
user := new(model.User)
30+
c.Bind(user)
31+
32+
dbuser, err := util.GetUser()
33+
if err != nil {
34+
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"})
35+
}
36+
37+
if user.Username == dbuser.Username && user.Password == dbuser.Password {
38+
// TODO: refresh the token
39+
sess, _ := session.Get("session", c)
40+
sess.Options = &sessions.Options{
41+
Path: "/",
42+
MaxAge: 86400,
43+
HttpOnly: true,
44+
}
45+
46+
// set session_token
47+
tokenUID := xid.New().String()
48+
sess.Values["username"] = user.Username
49+
sess.Values["session_token"] = tokenUID
50+
sess.Save(c.Request(), c.Response())
51+
52+
// set session_token in cookie
53+
cookie := new(http.Cookie)
54+
cookie.Name = "session_token"
55+
cookie.Value = tokenUID
56+
cookie.Expires = time.Now().Add(24 * time.Hour)
57+
c.SetCookie(cookie)
58+
59+
return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Logged in successfully"})
60+
}
61+
62+
return c.JSON(http.StatusUnauthorized, jsonHTTPResponse{false, "Invalid credentials"})
63+
}
64+
}
65+
66+
// Logout to log a user out
67+
func Logout() echo.HandlerFunc {
68+
return func(c echo.Context) error {
69+
clearSession(c)
70+
return c.Redirect(http.StatusTemporaryRedirect, "/login")
71+
}
72+
}
73+
2474
// WireGuardClients handler
2575
func WireGuardClients() echo.HandlerFunc {
2676
return func(c echo.Context) error {
77+
// access validation
78+
validSession(c)
79+
2780
clientDataList, err := util.GetClients(true)
2881
if err != nil {
2982
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, fmt.Sprintf("Cannot get client list: %v", err)})
3083
}
3184

3285
return c.Render(http.StatusOK, "clients.html", map[string]interface{}{
3386
"baseData": model.BaseData{Active: ""},
87+
"username": currentUser(c),
3488
"clientDataList": clientDataList,
3589
})
3690
}
@@ -39,6 +93,9 @@ func WireGuardClients() echo.HandlerFunc {
3993
// NewClient handler
4094
func NewClient() echo.HandlerFunc {
4195
return func(c echo.Context) error {
96+
// access validation
97+
validSession(c)
98+
4299
client := new(model.Client)
43100
c.Bind(client)
44101

@@ -93,6 +150,9 @@ func NewClient() echo.HandlerFunc {
93150
// SetClientStatus handler to enable / disable a client
94151
func SetClientStatus() echo.HandlerFunc {
95152
return func(c echo.Context) error {
153+
// access validation
154+
validSession(c)
155+
96156
data := make(map[string]interface{})
97157
err := json.NewDecoder(c.Request().Body).Decode(&data)
98158

@@ -125,6 +185,9 @@ func SetClientStatus() echo.HandlerFunc {
125185
// RemoveClient handler
126186
func RemoveClient() echo.HandlerFunc {
127187
return func(c echo.Context) error {
188+
// access validation
189+
validSession(c)
190+
128191
client := new(model.Client)
129192
c.Bind(client)
130193

@@ -148,13 +211,17 @@ func RemoveClient() echo.HandlerFunc {
148211
// WireGuardServer handler
149212
func WireGuardServer() echo.HandlerFunc {
150213
return func(c echo.Context) error {
214+
// access validation
215+
validSession(c)
216+
151217
server, err := util.GetServer()
152218
if err != nil {
153219
log.Error("Cannot get server config: ", err)
154220
}
155221

156222
return c.Render(http.StatusOK, "server.html", map[string]interface{}{
157223
"baseData": model.BaseData{Active: "wg-server"},
224+
"username": currentUser(c),
158225
"serverInterface": server.Interface,
159226
"serverKeyPair": server.KeyPair,
160227
})
@@ -164,6 +231,9 @@ func WireGuardServer() echo.HandlerFunc {
164231
// WireGuardServerInterfaces handler
165232
func WireGuardServerInterfaces() echo.HandlerFunc {
166233
return func(c echo.Context) error {
234+
// access validation
235+
validSession(c)
236+
167237
serverInterface := new(model.ServerInterface)
168238
c.Bind(serverInterface)
169239

@@ -192,6 +262,9 @@ func WireGuardServerInterfaces() echo.HandlerFunc {
192262
// WireGuardServerKeyPair handler to generate private and public keys
193263
func WireGuardServerKeyPair() echo.HandlerFunc {
194264
return func(c echo.Context) error {
265+
// access validation
266+
validSession(c)
267+
195268
// gen Wireguard key pair
196269
key, err := wgtypes.GeneratePrivateKey()
197270
if err != nil {
@@ -221,13 +294,17 @@ func WireGuardServerKeyPair() echo.HandlerFunc {
221294
// GlobalSettings handler
222295
func GlobalSettings() echo.HandlerFunc {
223296
return func(c echo.Context) error {
297+
// access validation
298+
validSession(c)
299+
224300
globalSettings, err := util.GetGlobalSettings()
225301
if err != nil {
226302
log.Error("Cannot get global settings: ", err)
227303
}
228304

229305
return c.Render(http.StatusOK, "global_settings.html", map[string]interface{}{
230306
"baseData": model.BaseData{Active: "global-settings"},
307+
"username": currentUser(c),
231308
"globalSettings": globalSettings,
232309
})
233310
}
@@ -236,6 +313,9 @@ func GlobalSettings() echo.HandlerFunc {
236313
// GlobalSettingSubmit handler to update the global settings
237314
func GlobalSettingSubmit() echo.HandlerFunc {
238315
return func(c echo.Context) error {
316+
// access validation
317+
validSession(c)
318+
239319
globalSettings := new(model.GlobalSetting)
240320
c.Bind(globalSettings)
241321

@@ -264,6 +344,9 @@ func GlobalSettingSubmit() echo.HandlerFunc {
264344
// MachineIPAddresses handler to get local interface ip addresses
265345
func MachineIPAddresses() echo.HandlerFunc {
266346
return func(c echo.Context) error {
347+
// access validation
348+
validSession(c)
349+
267350
// get private ip addresses
268351
interfaceList, err := util.GetInterfaceIPs()
269352
if err != nil {
@@ -287,6 +370,9 @@ func MachineIPAddresses() echo.HandlerFunc {
287370
// SuggestIPAllocation handler to get the list of ip address for client
288371
func SuggestIPAllocation() echo.HandlerFunc {
289372
return func(c echo.Context) error {
373+
// access validation
374+
validSession(c)
375+
290376
server, err := util.GetServer()
291377
if err != nil {
292378
log.Error("Cannot fetch server config from database: ", err)
@@ -317,6 +403,9 @@ func SuggestIPAllocation() echo.HandlerFunc {
317403
// ApplyServerConfig handler to write config file and restart Wireguard server
318404
func ApplyServerConfig() echo.HandlerFunc {
319405
return func(c echo.Context) error {
406+
// access validation
407+
validSession(c)
408+
320409
server, err := util.GetServer()
321410
if err != nil {
322411
log.Error("Cannot get server config: ", err)

handler/session.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package handler
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/labstack/echo-contrib/session"
8+
"github.com/labstack/echo/v4"
9+
)
10+
11+
// validSession to redirect user to the login page if they are not
12+
// authenticated or session expired.
13+
func validSession(c echo.Context) {
14+
sess, _ := session.Get("session", c)
15+
cookie, err := c.Cookie("session_token")
16+
if err != nil || sess.Values["session_token"] != cookie.Value {
17+
nextURL := c.Request().URL
18+
if nextURL != nil {
19+
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/login?next=%s", c.Request().URL))
20+
} else {
21+
c.Redirect(http.StatusTemporaryRedirect, "/login")
22+
}
23+
}
24+
}
25+
26+
// currentUser to get username of logged in user
27+
func currentUser(c echo.Context) string {
28+
sess, _ := session.Get("session", c)
29+
username := fmt.Sprintf("%s", sess.Values["username"])
30+
return username
31+
}
32+
33+
// clearSession to remove current session
34+
func clearSession(c echo.Context) {
35+
sess, _ := session.Get("session", c)
36+
sess.Values["username"] = ""
37+
sess.Values["session_token"] = ""
38+
sess.Save(c.Request(), c.Response())
39+
}

main.go

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ func main() {
2020

2121
app.GET("/", handler.WireGuardClients())
2222
app.GET("/login", handler.LoginPage())
23+
app.POST("/login", handler.Login())
24+
app.GET("/logout", handler.Logout())
2325
app.POST("/new-client", handler.NewClient())
2426
app.POST("/client/set-status", handler.SetClientStatus())
2527
app.POST("/remove-client", handler.RemoveClient())

model/user.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package model
2+
3+
// User model
4+
type User struct {
5+
Username string `json:"username"`
6+
Password string `json:"password"`
7+
}

router/router.go

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"io"
66
"text/template"
77

8+
"github.com/gorilla/sessions"
9+
"github.com/labstack/echo-contrib/session"
810
"github.com/labstack/echo/v4"
911
"github.com/labstack/echo/v4/middleware"
1012
"github.com/labstack/gommon/log"
@@ -32,6 +34,8 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c
3234
// New function
3335
func New() *echo.Echo {
3436
e := echo.New()
37+
e.Use(session.Middleware(sessions.NewCookieStore([]byte("secret"))))
38+
3539
templates := make(map[string]*template.Template)
3640
templates["login.html"] = template.Must(template.ParseFiles("templates/login.html"))
3741
templates["clients.html"] = template.Must(template.ParseFiles("templates/clients.html", "templates/base.html"))

templates/base.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
<button style="margin-left: 0.5em;" type="button" class="btn btn-outline-danger btn-sm" data-toggle="modal"
6565
data-target="#modal_apply_config"><i class="nav-icon fas fa-check"></i> Apply
6666
Config</button>
67+
<button onclick="location.href='/logout';" style="margin-left: 0.5em;" type="button"
68+
class="btn btn-outline-danger btn-sm"><i class="nav-icon fas fa-sign-out-alt"></i> Logout</button>
6769
</nav>
6870
</nav>
6971
<!-- /.navbar -->
@@ -82,7 +84,7 @@
8284
<!-- Sidebar user (optional) -->
8385
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
8486
<div class="image">
85-
<img src="static/dist/img/user2-160x160.jpg" class="img-circle elevation-2">
87+
<i class="nav-icon fas fa-2x fa-user"></i>
8688
</div>
8789
<div class="info">
8890
<a href="#" class="d-block">{{template "username" .}}</a>

templates/clients.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
{{end}}
1414

1515
{{define "username"}}
16-
Admin
16+
{{ .username }}
1717
{{end}}
1818

1919
{{define "page_title"}}

templates/global_settings.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
{{end}}
77

88
{{define "username"}}
9-
Admin
9+
{{ .username }}
1010
{{end}}
1111

1212
{{define "page_title"}}

0 commit comments

Comments
 (0)