diff --git a/Dockerfile b/Dockerfile
index a30cefe..70223c5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,6 +10,7 @@ ARG BUILD_DEPENDENCIES="npm \
# Get dependencies
RUN apk add --update --no-cache ${BUILD_DEPENDENCIES}
+#RUN apt install ${BUILD_DEPENDENCIES}
WORKDIR /build
@@ -20,8 +21,7 @@ COPY package.json /build
COPY yarn.lock /build
# Prepare assets
-RUN yarn install --pure-lockfile --production && \
- yarn cache clean
+RUN yarn install --pure-lockfile --production && yarn cache clean
# Move admin-lte dist
RUN mkdir -p assets/dist/js assets/dist/css && \
diff --git a/custom/js/helper.js b/custom/js/helper.js
index 86f6dc7..50b1d76 100644
--- a/custom/js/helper.js
+++ b/custom/js/helper.js
@@ -78,6 +78,35 @@ function renderClientList(data) {
});
}
+function renderUserList(data) {
+ $.each(data, function(index, obj) {
+ // render client status css tag style
+ let clientStatusHtml = '>'
+
+ // render client html content
+ let html = `
+
+
+
+
+
+
+
+
+
+
${obj.username}
+
${obj.admin? 'Administrator':'Manager'}
+
+
+
`
+
+ // add the client html elements to the list
+ $('#users-list').append(html);
+ });
+}
+
+
function prettyDateTime(timeStr) {
const dt = new Date(timeStr);
const offsetMs = dt.getTimezoneOffset() * 60 * 1000;
diff --git a/handler/routes.go b/handler/routes.go
index 3ddbb2d..ddab14f 100644
--- a/handler/routes.go
+++ b/handler/routes.go
@@ -42,39 +42,54 @@ func LoginPage() echo.HandlerFunc {
// Login for signing in handler
func Login(db store.IStore) echo.HandlerFunc {
return func(c echo.Context) error {
- user := new(model.User)
- c.Bind(user)
+ data := make(map[string]interface{})
+ err := json.NewDecoder(c.Request().Body).Decode(&data)
- dbuser, err := db.GetUser()
+ if err != nil {
+ return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Bad post data"})
+ }
+
+ username := data["username"].(string)
+ password := data["password"].(string)
+ rememberMe := data["rememberMe"].(bool)
+
+ dbuser, err := db.GetUserByName(username)
if err != nil {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"})
}
- userCorrect := subtle.ConstantTimeCompare([]byte(user.Username), []byte(dbuser.Username)) == 1
+ userCorrect := subtle.ConstantTimeCompare([]byte(username), []byte(dbuser.Username)) == 1
var passwordCorrect bool
if dbuser.PasswordHash != "" {
- match, err := util.VerifyHash(dbuser.PasswordHash, user.Password)
+ match, err := util.VerifyHash(dbuser.PasswordHash, password)
if err != nil {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify password"})
}
passwordCorrect = match
} else {
- passwordCorrect = subtle.ConstantTimeCompare([]byte(user.Password), []byte(dbuser.Password)) == 1
+ passwordCorrect = subtle.ConstantTimeCompare([]byte(password), []byte(dbuser.Password)) == 1
}
if userCorrect && passwordCorrect {
// TODO: refresh the token
+ ageMax := 0
+ expiration := time.Now().Add(24 * time.Hour)
+ if rememberMe {
+ ageMax = 86400
+ expiration.Add(144 * time.Hour)
+ }
sess, _ := session.Get("session", c)
sess.Options = &sessions.Options{
Path: util.BasePath,
- MaxAge: 86400,
+ MaxAge: ageMax,
HttpOnly: true,
}
// set session_token
tokenUID := xid.New().String()
- sess.Values["username"] = user.Username
+ sess.Values["username"] = dbuser.Username
+ sess.Values["admin"] = dbuser.Admin
sess.Values["session_token"] = tokenUID
sess.Save(c.Request(), c.Response())
@@ -82,7 +97,7 @@ func Login(db store.IStore) echo.HandlerFunc {
cookie := new(http.Cookie)
cookie.Name = "session_token"
cookie.Value = tokenUID
- cookie.Expires = time.Now().Add(24 * time.Hour)
+ cookie.Expires = expiration
c.SetCookie(cookie)
return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Logged in successfully"})
@@ -92,6 +107,36 @@ func Login(db store.IStore) echo.HandlerFunc {
}
}
+// GetClients handler return a JSON list of Wireguard client data
+func GetUsers(db store.IStore) echo.HandlerFunc {
+ return func(c echo.Context) error {
+
+ clientDataList, err := db.GetUsers()
+ if err != nil {
+ return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
+ false, fmt.Sprintf("Cannot get user list: %v", err),
+ })
+ }
+
+ return c.JSON(http.StatusOK, clientDataList)
+ }
+}
+
+// GetClient handler returns a JSON object of Wireguard client data
+func GetUser(db store.IStore) echo.HandlerFunc {
+ return func(c echo.Context) error {
+
+ username := c.Param("username")
+
+ userData, err := db.GetUserByName(username)
+ if err != nil {
+ return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "User not found"})
+ }
+
+ return c.JSON(http.StatusOK, userData)
+ }
+}
+
// Logout to log a user out
func Logout() echo.HandlerFunc {
return func(c echo.Context) error {
@@ -103,21 +148,23 @@ func Logout() echo.HandlerFunc {
// LoadProfile to load user information
func LoadProfile(db store.IStore) echo.HandlerFunc {
return func(c echo.Context) error {
-
- userInfo, err := db.GetUser()
- if err != nil {
- log.Error("Cannot get user information: ", err)
- }
-
return c.Render(http.StatusOK, "profile.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "profile", CurrentUser: currentUser(c)},
- "userInfo": userInfo,
+ "baseData": model.BaseData{Active: "profile", CurrentUser: currentUser(c), Admin: isAdmin(c)},
+ })
+ }
+}
+
+// WireGuardClients handler
+func UsersSettings(db store.IStore) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ return c.Render(http.StatusOK, "users_settings.html", map[string]interface{}{
+ "baseData": model.BaseData{Active: "users-settings", CurrentUser: currentUser(c), Admin: isAdmin(c)},
})
}
}
// UpdateProfile to update user information
-func UpdateProfile(db store.IStore) echo.HandlerFunc {
+func UpdateUser(db store.IStore) echo.HandlerFunc {
return func(c echo.Context) error {
data := make(map[string]interface{})
err := json.NewDecoder(c.Request().Body).Decode(&data)
@@ -128,8 +175,10 @@ func UpdateProfile(db store.IStore) echo.HandlerFunc {
username := data["username"].(string)
password := data["password"].(string)
+ previousUsername := data["previous_username"].(string)
+ admin := data["admin"].(bool)
- user, err := db.GetUser()
+ user, err := db.GetUserByName(previousUsername)
if err != nil {
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()})
}
@@ -140,6 +189,13 @@ func UpdateProfile(db store.IStore) echo.HandlerFunc {
user.Username = username
}
+ if username != previousUsername {
+ _, err := db.GetUserByName(username)
+ if err == nil {
+ return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "This username is taken"})
+ }
+ }
+
if password != "" {
hash, err := util.HashPassword(password)
if err != nil {
@@ -147,13 +203,93 @@ func UpdateProfile(db store.IStore) echo.HandlerFunc {
}
user.PasswordHash = hash
}
+ user.Admin = admin
+
+ if err := db.DeleteUser(previousUsername); err != nil {
+ return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
+ }
+ if err := db.SaveUser(user); err != nil {
+ return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
+ }
+ log.Infof("Updated user information successfully")
+
+ if previousUsername == currentUser(c) {
+ setUser(c, user.Username, user.Admin)
+ }
+
+ return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated user information successfully"})
+ }
+}
+
+// UpdateProfile to update user information
+func CreateUser(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 user model.User
+ username := data["username"].(string)
+ password := data["password"].(string)
+ admin := data["admin"].(bool)
+
+ if username == "" {
+ return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
+ } else {
+ user.Username = username
+ }
+
+ {
+ _, err := db.GetUserByName(username)
+ if err == nil {
+ return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "This username is taken"})
+ }
+ }
+
+ hash, err := util.HashPassword(password)
+ if err != nil {
+ return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
+ }
+ user.PasswordHash = hash
+
+ user.Admin = admin
if err := db.SaveUser(user); err != nil {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
}
- log.Infof("Updated admin user information successfully")
+ log.Infof("Created user successfully")
- return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated admin user information successfully"})
+ return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Created user successfully"})
+ }
+}
+
+// RemoveClient handler
+func RemoveUser(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"})
+ }
+
+ username := data["username"].(string)
+ // delete client from database
+
+ if err := db.DeleteUser(username); err != nil {
+ log.Error("Cannot delete user: ", err)
+ return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot delete user from database"})
+ }
+
+ log.Infof("Removed user: %s", username)
+ if username == currentUser(c) {
+ log.Infof("You removed yourself, killing session")
+ clearSession(c)
+ }
+ return c.JSON(http.StatusOK, jsonHTTPResponse{true, "User removed"})
}
}
@@ -169,7 +305,7 @@ func WireGuardClients(db store.IStore) echo.HandlerFunc {
}
return c.Render(http.StatusOK, "clients.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "", CurrentUser: currentUser(c)},
+ "baseData": model.BaseData{Active: "", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"clientDataList": clientDataList,
})
}
@@ -522,7 +658,7 @@ func WireGuardServer(db store.IStore) echo.HandlerFunc {
}
return c.Render(http.StatusOK, "server.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "wg-server", CurrentUser: currentUser(c)},
+ "baseData": model.BaseData{Active: "wg-server", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"serverInterface": server.Interface,
"serverKeyPair": server.KeyPair,
})
@@ -590,7 +726,7 @@ func GlobalSettings(db store.IStore) echo.HandlerFunc {
}
return c.Render(http.StatusOK, "global_settings.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "global-settings", CurrentUser: currentUser(c)},
+ "baseData": model.BaseData{Active: "global-settings", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"globalSettings": globalSettings,
})
}
@@ -618,7 +754,7 @@ func Status(db store.IStore) echo.HandlerFunc {
wgClient, err := wgctrl.New()
if err != nil {
return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
+ "baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"error": err.Error(),
"devices": nil,
})
@@ -627,7 +763,7 @@ func Status(db store.IStore) echo.HandlerFunc {
devices, err := wgClient.Devices()
if err != nil {
return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
+ "baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"error": err.Error(),
"devices": nil,
})
@@ -639,7 +775,7 @@ func Status(db store.IStore) echo.HandlerFunc {
clients, err := db.GetClients(false)
if err != nil {
return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
+ "baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"error": err.Error(),
"devices": nil,
})
@@ -676,7 +812,7 @@ func Status(db store.IStore) echo.HandlerFunc {
}
return c.Render(http.StatusOK, "status.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
+ "baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"devices": devicesVm,
"error": "",
})
@@ -790,6 +926,12 @@ func ApplyServerConfig(db store.IStore, tmplBox *rice.Box) echo.HandlerFunc {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get client config"})
}
+ users, err := db.GetUsers()
+ if err != nil {
+ log.Error("Cannot get users config: ", err)
+ return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get users config"})
+ }
+
settings, err := db.GetGlobalSettings()
if err != nil {
log.Error("Cannot get global settings: ", err)
@@ -797,7 +939,7 @@ func ApplyServerConfig(db store.IStore, tmplBox *rice.Box) echo.HandlerFunc {
}
// Write config file
- err = util.WriteWireGuardServerConfig(tmplBox, server, clients, settings)
+ err = util.WriteWireGuardServerConfig(tmplBox, server, clients, users, settings)
if err != nil {
log.Error("Cannot apply server config: ", err)
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
diff --git a/handler/routes_wake_on_lan.go b/handler/routes_wake_on_lan.go
index 40cd387..43a6186 100644
--- a/handler/routes_wake_on_lan.go
+++ b/handler/routes_wake_on_lan.go
@@ -37,7 +37,7 @@ func GetWakeOnLanHosts(db store.IStore) echo.HandlerFunc {
}
err = c.Render(http.StatusOK, "wake_on_lan_hosts.html", map[string]interface{}{
- "baseData": model.BaseData{Active: "wake_on_lan_hosts", CurrentUser: currentUser(c)},
+ "baseData": model.BaseData{Active: "wake_on_lan_hosts", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"hosts": hosts,
"error": "",
})
diff --git a/handler/session.go b/handler/session.go
index 9975e0d..689d507 100644
--- a/handler/session.go
+++ b/handler/session.go
@@ -14,15 +14,24 @@ func ValidSession(next echo.HandlerFunc) echo.HandlerFunc {
if !isValidSession(c) {
nextURL := c.Request().URL
if nextURL != nil && c.Request().Method == http.MethodGet {
- return c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf(util.BasePath + "/login?next=%s", c.Request().URL))
+ return c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf(util.BasePath+"/login?next=%s", c.Request().URL))
} else {
- return c.Redirect(http.StatusTemporaryRedirect, util.BasePath + "/login")
+ return c.Redirect(http.StatusTemporaryRedirect, util.BasePath+"/login")
}
}
return next(c)
}
}
+func NeedsAdmin(next echo.HandlerFunc) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ if !isAdmin(c) {
+ return c.Redirect(http.StatusTemporaryRedirect, util.BasePath+"/")
+ }
+ return next(c)
+ }
+}
+
func isValidSession(c echo.Context) bool {
if util.DisableLogin {
return true
@@ -46,10 +55,29 @@ func currentUser(c echo.Context) string {
return username
}
+// currentUser to get username of logged in user
+func isAdmin(c echo.Context) bool {
+ if util.DisableLogin {
+ return true
+ }
+
+ sess, _ := session.Get("session", c)
+ admin := fmt.Sprintf("%t", sess.Values["admin"])
+ return admin == "true"
+}
+
+func setUser(c echo.Context, username string, admin bool) {
+ sess, _ := session.Get("session", c)
+ sess.Values["username"] = username
+ sess.Values["admin"] = admin
+ sess.Save(c.Request(), c.Response())
+}
+
// clearSession to remove current session
func clearSession(c echo.Context) {
sess, _ := session.Get("session", c)
sess.Values["username"] = ""
+ sess.Values["admin"] = false
sess.Values["session_token"] = ""
sess.Save(c.Request(), c.Response())
}
diff --git a/main.go b/main.go
index 3f0cd13..aefc0bb 100644
--- a/main.go
+++ b/main.go
@@ -137,7 +137,12 @@ func main() {
app.POST(util.BasePath+"/login", handler.Login(db))
app.GET(util.BasePath+"/logout", handler.Logout(), handler.ValidSession)
app.GET(util.BasePath+"/profile", handler.LoadProfile(db), handler.ValidSession)
- app.POST(util.BasePath+"/profile", handler.UpdateProfile(db), handler.ValidSession)
+ app.GET(util.BasePath+"/users-settings", handler.UsersSettings(db), handler.ValidSession, handler.NeedsAdmin)
+ app.POST(util.BasePath+"/update-user", handler.UpdateUser(db), handler.ValidSession)
+ app.POST(util.BasePath+"/create-user", handler.CreateUser(db), handler.ValidSession, handler.NeedsAdmin)
+ app.POST(util.BasePath+"/remove-user", handler.RemoveUser(db), handler.ValidSession, handler.NeedsAdmin)
+ app.GET(util.BasePath+"/getusers", handler.GetUsers(db), handler.ValidSession, handler.NeedsAdmin)
+ app.GET(util.BasePath+"/api/user/:username", handler.GetUser(db), handler.ValidSession)
}
var sendmail emailer.Emailer
@@ -154,11 +159,12 @@ func main() {
app.POST(util.BasePath+"/client/set-status", handler.SetClientStatus(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/remove-client", handler.RemoveClient(db), handler.ValidSession, handler.ContentTypeJson)
app.GET(util.BasePath+"/download", handler.DownloadClient(db), handler.ValidSession)
- app.GET(util.BasePath+"/wg-server", handler.WireGuardServer(db), handler.ValidSession)
- 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.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson)
+ app.GET(util.BasePath+"/wg-server", handler.WireGuardServer(db), handler.ValidSession, handler.NeedsAdmin)
+ app.POST(util.BasePath+"/wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
+ app.POST(util.BasePath+"/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
+ app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession, handler.NeedsAdmin)
+
+ app.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
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)
@@ -197,8 +203,13 @@ func initServerConfig(db store.IStore, tmplBox *rice.Box) {
log.Fatalf("Cannot get client config: ", err)
}
+ users, err := db.GetUsers()
+ if err != nil {
+ log.Fatalf("Cannot get user config: ", err)
+ }
+
// write config file
- err = util.WriteWireGuardServerConfig(tmplBox, server, clients, settings)
+ err = util.WriteWireGuardServerConfig(tmplBox, server, clients, users, settings)
if err != nil {
log.Fatalf("Cannot create server config: ", err)
}
diff --git a/model/misc.go b/model/misc.go
index 12d6906..d8f2cf5 100644
--- a/model/misc.go
+++ b/model/misc.go
@@ -10,4 +10,5 @@ type Interface struct {
type BaseData struct {
Active string
CurrentUser string
+ Admin bool
}
diff --git a/model/user.go b/model/user.go
index 711ebd1..71f4d13 100644
--- a/model/user.go
+++ b/model/user.go
@@ -6,4 +6,5 @@ type User struct {
Password string `json:"password"`
// PasswordHash takes precedence over Password.
PasswordHash string `json:"password_hash"`
+ Admin bool `json:"admin"`
}
diff --git a/router/router.go b/router/router.go
index 0f9facc..f262243 100644
--- a/router/router.go
+++ b/router/router.go
@@ -83,6 +83,11 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec
log.Fatal(err)
}
+ tmplUsersSettingsString, err := tmplBox.String("users_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["users_settings.html"] = template.Must(template.New("users_settings").Funcs(funcs).Parse(tmplBaseString + tmplUsersSettingsString))
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..61d9cc5 100644
--- a/store/jsondb/jsondb.go
+++ b/store/jsondb/jsondb.go
@@ -42,7 +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 userPath string = path.Join(serverPath, "users.json")
+ var userPath string = path.Join(o.dbPath, "users")
// create directories if they do not exist
if _, err := os.Stat(clientPath); os.IsNotExist(err) {
os.MkdirAll(clientPath, os.ModePerm)
@@ -53,6 +53,9 @@ func (o *JsonDB) Init() error {
if _, err := os.Stat(wakeOnLanHostsPath); os.IsNotExist(err) {
os.MkdirAll(wakeOnLanHostsPath, os.ModePerm)
}
+ if _, err := os.Stat(userPath); os.IsNotExist(err) {
+ os.MkdirAll(userPath, os.ModePerm)
+ }
// server's interface
if _, err := os.Stat(serverInterfacePath); os.IsNotExist(err) {
@@ -103,9 +106,11 @@ func (o *JsonDB) Init() error {
}
// user info
- if _, err := os.Stat(userPath); os.IsNotExist(err) {
+ results, err := o.conn.ReadAll("users")
+ if err != nil || len(results) < 1 {
user := new(model.User)
user.Username = util.LookupEnvOrString(util.UsernameEnvVar, util.DefaultUsername)
+ user.Admin = util.DefaultIsAdmin
user.PasswordHash = util.LookupEnvOrString(util.PasswordHashEnvVar, "")
if user.PasswordHash == "" {
plaintext := util.LookupEnvOrString(util.PasswordEnvVar, util.DefaultPassword)
@@ -115,7 +120,7 @@ func (o *JsonDB) Init() error {
}
user.PasswordHash = hash
}
- o.conn.Write("server", "users", user)
+ o.conn.Write("users", user.Username, user)
}
return nil
@@ -127,11 +132,48 @@ func (o *JsonDB) GetUser() (model.User, error) {
return user, o.conn.Read("server", "users", &user)
}
-// SaveUser func to user info to the database
-func (o *JsonDB) SaveUser(user model.User) error {
- return o.conn.Write("server", "users", user)
+// GetUsers func to query user info from the database
+func (o *JsonDB) GetUsers() ([]model.User, error) {
+ var users []model.User
+ results, err := o.conn.ReadAll("users")
+ if err != nil {
+ return users, err
+ }
+ for _, i := range results {
+ user := model.User{}
+
+ if err := json.Unmarshal([]byte(i), &user); err != nil {
+ return users, fmt.Errorf("cannot decode user json structure: %v", err)
+ }
+ users = append(users, user)
+
+ }
+ return users, err
}
+func (o *JsonDB) GetUserByName(username string) (model.User, error) {
+ user := model.User{}
+
+ if err := o.conn.Read("users", username, &user); err != nil {
+ return user, err
+ }
+
+ return user, nil
+}
+
+func (o *JsonDB) SaveUser(user model.User) error {
+ return o.conn.Write("users", user.Username, user)
+}
+
+func (o *JsonDB) DeleteUser(username string) error {
+ return o.conn.Delete("users", username)
+}
+
+//// SaveUser func to user info to the database
+//func (o *JsonDB) SaveUser(user model.User) error {
+// return o.conn.Write("server", "users", user)
+//}
+
// GetGlobalSettings func to query global settings from the database
func (o *JsonDB) GetGlobalSettings() (model.GlobalSetting, error) {
settings := model.GlobalSetting{}
@@ -213,7 +255,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 {
diff --git a/store/store.go b/store/store.go
index 86d6224..166bc3d 100644
--- a/store/store.go
+++ b/store/store.go
@@ -6,8 +6,10 @@ import (
type IStore interface {
Init() error
- GetUser() (model.User, error)
+ GetUsers() ([]model.User, error)
+ GetUserByName(username string) (model.User, error)
SaveUser(user model.User) error
+ DeleteUser(username string) error
GetGlobalSettings() (model.GlobalSetting, error)
GetServer() (model.Server, error)
GetClients(hasQRCode bool) ([]model.ClientData, error)
diff --git a/templates/base.html b/templates/base.html
index fd337a7..227e35d 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -88,7 +88,13 @@
{{if .baseData.CurrentUser}}
-
{{.baseData.CurrentUser}}
+
+ {{if .baseData.Admin}}
+
Administrator: {{.baseData.CurrentUser}}
+ {{else}}
+
Manager: {{.baseData.CurrentUser}}
+ {{end}}
+
{{else}}
Administrator
{{end}}
@@ -107,6 +113,8 @@
+
+ {{if .baseData.Admin}}
@@ -115,6 +123,8 @@
+
+
@@ -124,6 +134,16 @@
+
+
+
+
+ Users Settings
+
+
+
+ {{end}}
+
diff --git a/templates/login.html b/templates/login.html
index c75aa39..db0ba24 100644
--- a/templates/login.html
+++ b/templates/login.html
@@ -99,7 +99,11 @@
$("#btn_login").click(function () {
const username = $("#username").val();
const password = $("#password").val();
- const data = {"username": username, "password": password}
+ let rememberMe = false;
+ if ($("#remember").is(':checked')){
+ rememberMe = true;
+ }
+ const data = {"username": username, "password": password, "rememberMe": rememberMe}
$.ajax({
cache: false,
diff --git a/templates/profile.html b/templates/profile.html
index c2d3b95..5b08745 100644
--- a/templates/profile.html
+++ b/templates/profile.html
@@ -31,7 +31,7 @@ Profile
+ value="">