add error handling for reading configuration #1

Merged
arya merged 9 commits from odyseusz/publapi:master into master 2024-11-04 20:20:30 +05:30
3 changed files with 80 additions and 71 deletions
Showing only changes of commit 2f4cbc6e1e - Show all commits

View File

@ -14,40 +14,54 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// SignupPage is the signup page handler type formValues struct {
Username string
Email string
SshPublicKey string
IPAddress string
CaptchaResponse string
}
type CaptchaResponse struct {
Success bool `json:"success"`
}
// SignupPage is the signup page handler
func SignupPage(c *fiber.Ctx) error { func SignupPage(c *fiber.Ctx) error {
SignupIP, SignupIPExists := os.LookupEnv("PUBLAPI_SIGNUP_IP") SignupIP, SignupIPExists := os.LookupEnv("PUBLAPI_SIGNUP_IP")
if SignupIPExists == true { if SignupIPExists {
if c.IP() != SignupIP { if c.IP() != SignupIP {
log.Info("Request made from invalid IP: ", c.IP()) log.Info("Request made from invalid IP: ", c.IP())
return c.SendStatus(fiber.StatusForbidden) return c.SendStatus(fiber.StatusForbidden)
} }
} }
username := c.FormValue("username")
email := c.FormValue("email")
ssh := c.FormValue("ssh")
ip := c.FormValue("ip")
captchaResp := c.FormValue("h-captcha-response")
if captchaResp == "" { formValues := formValues{
Username: c.FormValue("username"),
Email: c.FormValue("email"),
SshPublicKey: c.FormValue("ssh"),
IPAddress: c.FormValue("ip"),
CaptchaResponse: c.FormValue("h-captcha-response"),
}
if formValues.CaptchaResponse == "" {
log.Error("Nice try, but the registration won't work unless you answer the captcha.") log.Error("Nice try, but the registration won't work unless you answer the captcha.")
return c.SendStatus(fiber.StatusBadRequest) return c.SendStatus(fiber.StatusBadRequest)
} }
if username == "" || email == "" || ssh == "" || ip == "" { if formValues.Username == "" || formValues.Email == "" || formValues.SshPublicKey == "" || formValues.IPAddress == "" {
log.Error("username, email, ssh and ip must be filled", username, email, ssh, ip) log.Error("username, email, ssh and ip must be filled", formValues.Username, formValues.Email, formValues.SshPublicKey, formValues.IPAddress)
return c.SendStatus(fiber.StatusBadRequest) return c.SendStatus(fiber.StatusBadRequest)
} }
raid, ok := os.LookupEnv("PUBLAPI_RAID_MODE") raid, ok := os.LookupEnv("PUBLAPI_RAID_MODE")
if !ok || raid == "1" { if !ok || raid == "1" {
log.Warn( log.Warn(
"PUBLAPI_RAID_MODE is on, accepting every request as OK and not doing anything...\n User info: ", "PUBLAPI_RAID_MODE is on, accepting every request as OK and not doing anything...\n User info: ",
username, formValues.Username,
" ", " ",
email, formValues.Email,
" ", " ",
ip, formValues.IPAddress,
" ", " ",
) )
return c.SendStatus(fiber.StatusOK) return c.SendStatus(fiber.StatusOK)
@ -55,10 +69,10 @@ func SignupPage(c *fiber.Ctx) error {
// Check the captcha validation. // Check the captcha validation.
captchaSecret, ok := os.LookupEnv("PUBLAPI_CAPTCHA_SECRET") captchaSecret, _ := os.LookupEnv("PUBLAPI_CAPTCHA_SECRET")
params := url.Values{} params := url.Values{}
params.Add("response", captchaResp) params.Add("response", formValues.CaptchaResponse)
params.Add("secret", captchaSecret) params.Add("secret", captchaSecret)
body := strings.NewReader(params.Encode()) body := strings.NewReader(params.Encode())
@ -82,30 +96,27 @@ func SignupPage(c *fiber.Ctx) error {
sb := string(bod) sb := string(bod)
log.Info("Captcha response: ", sb) log.Info("Captcha response: ", sb)
type CaptchaResponse struct { captchaResponse := CaptchaResponse{}
Success bool `json:"success"`
}
var captchaResponse CaptchaResponse
err = json.Unmarshal([]byte(sb), &captchaResponse) err = json.Unmarshal([]byte(sb), &captchaResponse)
if err != nil { if err != nil {
log.Error("Error unmarshalling captcha response", err) log.Error("Error unmarshalling captcha response", err)
} }
if captchaResponse.Success == false { if !captchaResponse.Success {
log.Error("Captcha validation failed") log.Error("Captcha validation failed")
return c.JSON(fiber.Map{ return c.JSON(fiber.Map{
"username": username, "username": formValues.CaptchaResponse,
"message": "Sorry! But the captcha validation failed. Please try again.", "message": "Sorry, but the captcha validation failed. Please try again.",
"status": c.Response().StatusCode(), "status": c.Response().StatusCode(),
}) })
} else { } else {
// Check if user already exists // Check if user already exists
// We'll check the home folder and see if the username folder exists. // We'll check the home folder and see if the username folder exists.
_, err := os.Stat("/home/" + username) _, err := os.Stat("/home/" + formValues.Username)
if err == nil { if err == nil {
log.Error("User already exists : ", username) log.Error("User already exists : ", formValues.Username)
return c.JSON(fiber.Map{ return c.JSON(fiber.Map{
"username": username, "username": formValues.Username,
"message": "User already exists. Please choose a different username.", "message": "User already exists. Please choose a different username.",
"status": c.Response().StatusCode(), "status": c.Response().StatusCode(),
}) })
@ -113,28 +124,28 @@ func SignupPage(c *fiber.Ctx) error {
// create user file // create user file
f, err := os.Create("/var/publapi/users/" + username + ".sh") f, err := os.Create("/var/publapi/users/" + formValues.Username + ".sh")
if err != nil { if err != nil {
log.Error("Error creating user file", err) log.Error("Error creating user file", err)
return c.SendStatus(fiber.StatusInternalServerError) return c.SendStatus(fiber.StatusInternalServerError)
} }
defer f.Close() defer f.Close()
chmoderr := os.Chmod("/var/publapi/users/"+username+".sh", 0700) chmoderr := os.Chmod("/var/publapi/users/"+formValues.Username+".sh", 0700)
if chmoderr != nil { if chmoderr != nil {
log.Error(err) log.Error(err)
} }
Bashscript := strings.ReplaceAll(utils.Bashscript, "{{sshkey}}", ssh) bashScript := strings.ReplaceAll(utils.BashScript, "{{sshkey}}", formValues.SshPublicKey)
Bashscript = strings.ReplaceAll(Bashscript, "{{email}}", email) bashScript = strings.ReplaceAll(bashScript, "{{email}}", formValues.Email)
Bashscript = strings.ReplaceAll(Bashscript, "{{username}}", username) bashScript = strings.ReplaceAll(bashScript, "{{username}}", formValues.Username)
// write to file // write to file
_, err = f.WriteString(Bashscript) _, err = f.WriteString(bashScript)
if err != nil { if err != nil {
log.Error("Error writing to user file", err) log.Error("Error writing to user file", err)
return c.SendStatus(fiber.StatusInternalServerError) return c.SendStatus(fiber.StatusInternalServerError)
} }
log.Info( log.Info(
"Registration request for " + username + " has been submitted by the frontend and has been written to /var/publapi/users/" + username + ".sh", "Registration request for " + formValues.Username + " has been submitted by the frontend and has been written to /var/publapi/users/" + formValues.Username + ".sh",
) )
// send notification to user that their reg request was sent // send notification to user that their reg request was sent
@ -150,14 +161,14 @@ func SignupPage(c *fiber.Ctx) error {
shoutrrrUrl := os.Getenv("PUBLAPI_NOTIFY_SHOUTRRRURL") + os.Getenv("PUBLAPI_NOTIFY_ROOMS") shoutrrrUrl := os.Getenv("PUBLAPI_NOTIFY_SHOUTRRRURL") + os.Getenv("PUBLAPI_NOTIFY_ROOMS")
err = shoutrrr.Send( err = shoutrrr.Send(
shoutrrrUrl, shoutrrrUrl,
"New user signup! Please review /var/publapi/users/"+username+".sh to approve or deny the user. IP: "+ip+" Email: "+email, "New user signup! Please review /var/publapi/users/"+formValues.Username+".sh to approve or deny the user. IP: "+formValues.IPAddress+" Email: "+formValues.Email,
) )
if err != nil { if err != nil {
log.Error("Error sending notification to admins", err) log.Error("Error sending notification to admins", err)
//return c.SendStatus(fiber.StatusInternalServerError) //return c.SendStatus(fiber.StatusInternalServerError)
} }
return c.JSON(fiber.Map{ return c.JSON(fiber.Map{
"username": username, "username": formValues.Username,
"message": "User created! Please allow us 24 hours or more to review your account.", "message": "User created! Please allow us 24 hours or more to review your account.",
"status": c.Response().StatusCode(), "status": c.Response().StatusCode(),
}) })

View File

@ -16,14 +16,14 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
type Userstruct struct { type UserStruct struct {
Status int `json:"status"` Status int `json:"status"`
Online int `json:"online"` Online int `json:"online"`
Total int `json:"total"` Total int `json:"total"`
Users []Userinfo `json:"users"` Users []UserInfo `json:"users"`
} }
type Userinfo struct { type UserInfo struct {
Name string `json:"name"` Name string `json:"name"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
Desc string `json:"desc"` Desc string `json:"desc"`
@ -38,7 +38,7 @@ type Userinfo struct {
Loc string `json:"loc"` Loc string `json:"loc"`
} }
type ByAdminAndName []Userinfo type ByAdminAndName []UserInfo
func (a ByAdminAndName) Len() int { return len(a) } func (a ByAdminAndName) Len() int { return len(a) }
func (a ByAdminAndName) Less(i, j int) bool { func (a ByAdminAndName) Less(i, j int) bool {
@ -58,7 +58,7 @@ func UserError(message string, username string, err error) {
log.Error("error", message, log.Any("err", err), "username", username) log.Error("error", message, log.Any("err", err), "username", username)
} }
func userdata(username, usersonline, ops string) Userinfo { func userData(username, usersonline, ops string) UserInfo {
regex := "(^| )" + username + "($| )" regex := "(^| )" + username + "($| )"
isonline, err := regexp.MatchString(string(regex), string(usersonline)) isonline, err := regexp.MatchString(string(regex), string(usersonline))
if err != nil { if err != nil {
@ -80,7 +80,7 @@ func userdata(username, usersonline, ops string) Userinfo {
if error != nil { if error != nil {
if os.IsNotExist(error) { if os.IsNotExist(error) {
log.Error(username + " does not have a meta-info.toml") log.Error(username + " does not have a meta-info.toml")
var user Userinfo var user UserInfo
user.Name = username user.Name = username
user.Created, _ = strconv.Atoi(crdstr) user.Created, _ = strconv.Atoi(crdstr)
if isonline { if isonline {
@ -99,7 +99,7 @@ func userdata(username, usersonline, ops string) Userinfo {
viper.SetConfigFile(filename) viper.SetConfigFile(filename)
if err := viper.ReadInConfig(); err != nil { if err := viper.ReadInConfig(); err != nil {
log.Error("message", "Couldn't read a users meta-info.toml file.", "error", log.Any("err", err), "user", username) log.Error("message", "Couldn't read a users meta-info.toml file.", "error", log.Any("err", err), "user", username)
user := Userinfo{ user := UserInfo{
Name: username, Name: username,
} }
user.Created, _ = strconv.Atoi(crdstr) user.Created, _ = strconv.Atoi(crdstr)
@ -115,27 +115,25 @@ func userdata(username, usersonline, ops string) Userinfo {
} }
return user return user
} }
var user Userinfo user := UserInfo{
user = Userinfo{} Name: username,
user.Name = username FullName: viper.GetString("fullname"),
Capsule: viper.GetString("gemini"),
Website: viper.GetString("website"),
Desc: viper.GetString("description"),
Email: viper.GetString("email"),
Matrix: viper.GetString("matrix"),
Fediverse: viper.GetString("fediverse"),
Loc: viper.GetString("location"),
Op: false,
Online: false,
}
user.Created, _ = strconv.Atoi(crdstr) user.Created, _ = strconv.Atoi(crdstr)
user.FullName = viper.GetString("fullname")
user.Capsule = viper.GetString("gemini")
user.Website = viper.GetString("website")
user.Desc = viper.GetString("description")
user.Email = viper.GetString("email")
user.Matrix = viper.GetString("matrix")
user.Fediverse = viper.GetString("fediverse")
user.Loc = viper.GetString("location")
if isop { if isop {
user.Op = true user.Op = true
} else {
user.Op = false
} }
if isonline { if isonline {
user.Online = true user.Online = true
} else {
user.Online = false
} }
return user return user
} }
@ -148,18 +146,18 @@ func UsersPage(c *fiber.Ctx) error {
}) })
} }
// Get the number of users online // Get the number of users online
usersonline, err := exec.Command("bash", "-c", "/usr/bin/users").Output() usersOnline, err := exec.Command("bash", "-c", "/usr/bin/users").Output()
if err != nil { if err != nil {
log.Error("error", log.Any("error", err)) log.Error("error", log.Any("error", err))
return c.SendStatus(fiber.StatusInternalServerError) return c.SendStatus(fiber.StatusInternalServerError)
} }
usersonlinestr := string(usersonline) usersOnlineStr := string(usersOnline)
usersonlinededup := utils.Dedup(usersonlinestr) usersOnlineDedup := utils.Dedup(usersOnlineStr)
var output int var output int
if strings.Contains(usersonlinededup, " ") { if strings.Contains(usersOnlineDedup, " ") {
outputa := int(strings.Count(usersonlinededup, " ")) outputa := int(strings.Count(usersOnlineDedup, " "))
output = outputa + 1 output = outputa + 1
} else if usersonlinededup == "" { } else if usersOnlineDedup == "" {
output = 0 output = 0
} else { } else {
output = 1 output = 1
@ -181,23 +179,23 @@ func UsersPage(c *fiber.Ctx) error {
userstr2 := strings.TrimSuffix(userstr, "\n") userstr2 := strings.TrimSuffix(userstr, "\n")
usersarr := strings.Split(userstr2, "\n") usersarr := strings.Split(userstr2, "\n")
// Fill user info // Fill user info
var userinfostruct []Userinfo var userInfoStruct []UserInfo
for i := 0; i < len(usersarr); i++ { for i := 0; i < len(usersarr); i++ {
uname := string(usersarr[i]) uname := string(usersarr[i])
userinfostruct = append( userInfoStruct = append(
userinfostruct, userInfoStruct,
userdata( userData(
uname, uname,
strings.TrimSuffix(usersonlinededup, "\n"), strings.TrimSuffix(usersOnlineDedup, "\n"),
strings.TrimSuffix(opstr, "\n"), strings.TrimSuffix(opstr, "\n"),
), ),
) )
} }
sort.Sort(ByAdminAndName(userinfostruct)) sort.Sort(ByAdminAndName(userInfoStruct))
data := Userstruct{ data := UserStruct{
Status: c.Response().StatusCode(), Status: c.Response().StatusCode(),
Online: output, Online: output,
Users: userinfostruct, Users: userInfoStruct,
Total: int(strings.Count(userstr, "\n")), Total: int(strings.Count(userstr, "\n")),
} }
return c.JSON(data) return c.JSON(data)

View File

@ -6,7 +6,7 @@ import (
) )
//go:embed signup-script-template //go:embed signup-script-template
var Bashscript string var BashScript string
func Dedup(input string) string { func Dedup(input string) string {
unique := []string{} unique := []string{}