diff --git a/handler/routes.go b/handler/routes.go index a2ceeaf..c0413d4 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -8,6 +8,7 @@ import ( "io/fs" "net/http" "os" + "regexp" "sort" "strings" "time" @@ -26,6 +27,8 @@ import ( "github.com/ngoduykhanh/wireguard-ui/util" ) +var usernameRegexp = regexp.MustCompile("^\\w[\\w\\-.]*$") + // Health check handler func Health() echo.HandlerFunc { return func(c echo.Context) error { @@ -63,6 +66,10 @@ func Login(db store.IStore) echo.HandlerFunc { password := data["password"].(string) rememberMe := data["rememberMe"].(bool) + if !usernameRegexp.MatchString(username) { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"}) + } + dbuser, err := db.GetUserByName(username) if err != nil { return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"}) @@ -135,9 +142,12 @@ func GetUsers(db store.IStore) echo.HandlerFunc { // GetUser handler returns a JSON object of single user func GetUser(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - username := c.Param("username") + if !usernameRegexp.MatchString(username) { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"}) + } + if !isAdmin(c) && (username != currentUser(c)) { return c.JSON(http.StatusForbidden, jsonHTTPResponse{false, "Manager cannot access other user data"}) } @@ -200,12 +210,16 @@ func UpdateUser(db store.IStore) echo.HandlerFunc { admin = false } + if !usernameRegexp.MatchString(previousUsername) { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"}) + } + user, err := db.GetUserByName(previousUsername) if err != nil { return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()}) } - if username == "" { + if username == "" || !usernameRegexp.MatchString(username) { return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"}) } else { user.Username = username @@ -261,7 +275,7 @@ func CreateUser(db store.IStore) echo.HandlerFunc { password := data["password"].(string) admin := data["admin"].(bool) - if username == "" { + if username == "" || !usernameRegexp.MatchString(username) { return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"}) } else { user.Username = username @@ -303,6 +317,10 @@ func RemoveUser(db store.IStore) echo.HandlerFunc { username := data["username"].(string) + if !usernameRegexp.MatchString(username) { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"}) + } + if username == currentUser(c) { return c.JSON(http.StatusForbidden, jsonHTTPResponse{false, "User cannot delete itself"}) } @@ -357,6 +375,11 @@ func GetClient(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { clientID := c.Param("id") + + if _, err := xid.FromString(clientID); err != nil { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"}) + } + qrCodeSettings := model.QRCodeSettings{ Enabled: true, IncludeDNS: true, @@ -485,6 +508,10 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon c.Bind(&payload) // TODO validate email + if _, err := xid.FromString(payload.ID); err != nil { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"}) + } + qrCodeSettings := model.QRCodeSettings{ Enabled: true, IncludeDNS: true, @@ -536,6 +563,10 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { var _client model.Client c.Bind(&_client) + if _, err := xid.FromString(_client.ID); err != nil { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"}) + } + // validate client existence clientData, err := db.GetClientByID(_client.ID, model.QRCodeSettings{Enabled: false}) if err != nil { @@ -642,6 +673,10 @@ func SetClientStatus(db store.IStore) echo.HandlerFunc { clientID := data["id"].(string) status := data["status"].(bool) + if _, err := xid.FromString(clientID); err != nil { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"}) + } + clientData, err := db.GetClientByID(clientID, model.QRCodeSettings{Enabled: false}) if err != nil { return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()}) @@ -667,6 +702,10 @@ func DownloadClient(db store.IStore) echo.HandlerFunc { return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Missing clientid parameter"}) } + if _, err := xid.FromString(clientID); err != nil { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"}) + } + clientData, err := db.GetClientByID(clientID, model.QRCodeSettings{Enabled: false}) if err != nil { log.Errorf("Cannot generate client id %s config file for downloading: %v", clientID, err) @@ -700,6 +739,10 @@ func RemoveClient(db store.IStore) echo.HandlerFunc { client := new(model.Client) c.Bind(client) + if _, err := xid.FromString(client.ID); err != nil { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"}) + } + // delete client from database if err := db.DeleteClient(client.ID); err != nil { diff --git a/model/wake_on_lan_host.go b/model/wake_on_lan_host.go index 2d1ab73..73966f0 100644 --- a/model/wake_on_lan_host.go +++ b/model/wake_on_lan_host.go @@ -2,6 +2,7 @@ package model import ( "errors" + "net" "strings" "time" ) @@ -18,7 +19,13 @@ func (host WakeOnLanHost) ResolveResourceName() (string, error) { return "", errors.New("mac Address is Empty") } resourceName = strings.ToUpper(resourceName) - return strings.ReplaceAll(resourceName, ":", "-"), nil + resourceName = strings.ReplaceAll(resourceName, ":", "-") + + if _, err := net.ParseMAC(resourceName); err != nil { + return "", errors.New("invalid mac address") + } + + return resourceName, nil } const WakeOnLanHostCollectionName = "wake_on_lan_hosts" diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go index 4ccd9b7..b961e95 100644 --- a/store/jsondb/jsondb.go +++ b/store/jsondb/jsondb.go @@ -38,12 +38,12 @@ func New(dbPath string) (*JsonDB, error) { func (o *JsonDB) Init() error { var clientPath string = path.Join(o.dbPath, "clients") var serverPath string = path.Join(o.dbPath, "server") + var userPath string = path.Join(o.dbPath, "users") var wakeOnLanHostsPath string = path.Join(o.dbPath, "wake_on_lan_hosts") 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 hashesPath string = path.Join(serverPath, "hashes.json") - var userPath string = path.Join(serverPath, "users.json") // create directories if they do not exist if _, err := os.Stat(clientPath); os.IsNotExist(err) { @@ -52,12 +52,12 @@ func (o *JsonDB) Init() error { if _, err := os.Stat(serverPath); os.IsNotExist(err) { os.MkdirAll(serverPath, os.ModePerm) } - 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) } + if _, err := os.Stat(wakeOnLanHostsPath); os.IsNotExist(err) { + os.MkdirAll(wakeOnLanHostsPath, os.ModePerm) + } // server's interface if _, err := os.Stat(serverInterfacePath); os.IsNotExist(err) { @@ -149,12 +149,6 @@ func (o *JsonDB) Init() error { return nil } -// GetUser func to query user info from the database -func (o *JsonDB) GetUser() (model.User, error) { - user := model.User{} - return user, o.conn.Read("server", "users", &user) -} - // GetUsers func to get all users from the database func (o *JsonDB) GetUsers() ([]model.User, error) { var users []model.User