2023-01-07 21:03:51 +05:30
package pages
import (
2023-07-11 02:53:29 +05:30
"encoding/json"
2024-02-27 19:35:13 +05:30
"io"
2023-07-11 02:53:29 +05:30
"net/http"
"net/url"
"os"
"strings"
2024-09-29 18:05:33 +05:30
log "log/slog"
2023-01-21 22:19:17 +05:30
"github.com/ProjectSegfault/publapi/utils"
2023-01-07 21:03:51 +05:30
"github.com/containrrr/shoutrrr"
2023-02-05 19:00:47 +05:30
"github.com/gofiber/fiber/v2"
2023-01-07 21:03:51 +05:30
)
2024-09-29 00:20:57 +05:30
type formValues struct {
Username string
Email string
SshPublicKey string
IPAddress string
CaptchaResponse string
}
type CaptchaResponse struct {
Success bool ` json:"success" `
}
2023-07-11 02:53:29 +05:30
2024-09-29 18:05:33 +05:30
func CaptchaCheck ( response string , secret string ) bool {
// Check the captcha validation.
params := url . Values { }
params . Add ( "response" , response )
params . Add ( "secret" , secret )
body := strings . NewReader ( params . Encode ( ) )
req , err := http . NewRequest ( "POST" , "https://hcaptcha.com/siteverify" , body )
if err != nil {
log . Error ( "couldn't make request to hCaptcha verification API" , log . Any ( "err" , err ) )
return false
}
req . Header . Set ( "Content-Type" , "application/x-www-form-urlencoded" )
resp , err := http . DefaultClient . Do ( req )
if err != nil {
log . Error ( "couldn't do request to hCaptcha verification API" , log . Any ( "err" , err ) )
return false
}
defer resp . Body . Close ( )
bod , err := io . ReadAll ( resp . Body )
if err != nil {
log . Error ( "couldn't read response body" , log . Any ( "err" , err ) )
return false
}
sb := string ( bod )
captchaResponse := CaptchaResponse { }
err = json . Unmarshal ( [ ] byte ( sb ) , & captchaResponse )
if err != nil {
log . Error ( "couldn't unmarshal hCaptcha response into a CaptchaResponse struct" , log . Any ( "err" , err ) )
}
return captchaResponse . Success
}
2024-09-29 00:20:57 +05:30
// SignupPage is the signup page handler
2023-01-07 21:03:51 +05:30
func SignupPage ( c * fiber . Ctx ) error {
2023-02-18 15:20:24 +05:30
SignupIP , SignupIPExists := os . LookupEnv ( "PUBLAPI_SIGNUP_IP" )
2024-09-29 00:20:57 +05:30
if SignupIPExists {
2023-02-18 15:20:24 +05:30
if c . IP ( ) != SignupIP {
log . Info ( "Request made from invalid IP: " , c . IP ( ) )
return c . SendStatus ( fiber . StatusForbidden )
}
}
2023-07-11 02:53:29 +05:30
2024-09-29 00:20:57 +05:30
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 == "" {
2023-07-11 02:53:29 +05:30
return c . SendStatus ( fiber . StatusBadRequest )
}
2024-09-29 00:20:57 +05:30
if formValues . Username == "" || formValues . Email == "" || formValues . SshPublicKey == "" || formValues . IPAddress == "" {
log . Error ( "username, email, ssh and ip must be filled" , formValues . Username , formValues . Email , formValues . SshPublicKey , formValues . IPAddress )
2023-01-17 20:36:47 +05:30
return c . SendStatus ( fiber . StatusBadRequest )
}
2023-05-27 15:54:23 +05:30
raid , ok := os . LookupEnv ( "PUBLAPI_RAID_MODE" )
if ! ok || raid == "1" {
2023-07-11 02:53:29 +05:30
log . Warn (
"PUBLAPI_RAID_MODE is on, accepting every request as OK and not doing anything...\n User info: " ,
2024-09-29 00:20:57 +05:30
formValues . Username ,
2023-07-11 02:53:29 +05:30
" " ,
2024-09-29 00:20:57 +05:30
formValues . Email ,
2023-07-11 02:53:29 +05:30
" " ,
2024-09-29 00:20:57 +05:30
formValues . IPAddress ,
2023-07-11 02:53:29 +05:30
" " ,
)
2023-05-27 15:54:23 +05:30
return c . SendStatus ( fiber . StatusOK )
}
2023-01-07 21:03:51 +05:30
2024-09-29 18:05:33 +05:30
// get captcha secret
2024-09-29 00:20:57 +05:30
captchaSecret , _ := os . LookupEnv ( "PUBLAPI_CAPTCHA_SECRET" )
2023-07-11 02:53:29 +05:30
2024-09-29 18:05:33 +05:30
if ! CaptchaCheck ( formValues . CaptchaResponse , captchaSecret ) {
2023-07-11 02:53:29 +05:30
log . Error ( "Captcha validation failed" )
2023-07-11 03:38:46 +05:30
return c . JSON ( fiber . Map {
2024-09-29 18:05:33 +05:30
"username" : formValues . Username ,
2024-09-29 00:20:57 +05:30
"message" : "Sorry, but the captcha validation failed. Please try again." ,
2023-07-11 03:38:46 +05:30
"status" : c . Response ( ) . StatusCode ( ) ,
} )
2023-07-11 02:53:29 +05:30
} else {
2024-02-27 19:35:13 +05:30
// Check if user already exists
// We'll check the home folder and see if the username folder exists.
2024-09-29 00:20:57 +05:30
_ , err := os . Stat ( "/home/" + formValues . Username )
2024-02-27 19:35:13 +05:30
if err == nil {
2024-09-29 00:20:57 +05:30
log . Error ( "User already exists : " , formValues . Username )
2024-02-27 19:35:13 +05:30
return c . JSON ( fiber . Map {
2024-09-29 00:20:57 +05:30
"username" : formValues . Username ,
2024-02-27 19:59:50 +05:30
"message" : "User already exists. Please choose a different username." ,
2024-02-27 19:35:13 +05:30
"status" : c . Response ( ) . StatusCode ( ) ,
} )
}
2024-02-27 19:37:27 +05:30
2023-07-11 02:53:29 +05:30
// create user file
2024-09-29 00:20:57 +05:30
f , err := os . Create ( "/var/publapi/users/" + formValues . Username + ".sh" )
2023-07-11 02:53:29 +05:30
if err != nil {
log . Error ( "Error creating user file" , err )
return c . SendStatus ( fiber . StatusInternalServerError )
}
defer f . Close ( )
2024-09-29 00:20:57 +05:30
chmoderr := os . Chmod ( "/var/publapi/users/" + formValues . Username + ".sh" , 0700 )
2023-07-11 02:53:29 +05:30
if chmoderr != nil {
2024-09-29 18:05:33 +05:30
UserError ( "couldn't chmod users script with permissions 0700" , formValues . Username , chmoderr )
2023-07-11 02:53:29 +05:30
}
2024-09-29 00:20:57 +05:30
bashScript := strings . ReplaceAll ( utils . BashScript , "{{sshkey}}" , formValues . SshPublicKey )
bashScript = strings . ReplaceAll ( bashScript , "{{email}}" , formValues . Email )
bashScript = strings . ReplaceAll ( bashScript , "{{username}}" , formValues . Username )
2023-07-11 02:53:29 +05:30
// write to file
2024-09-29 00:20:57 +05:30
_ , err = f . WriteString ( bashScript )
2023-07-11 02:53:29 +05:30
if err != nil {
log . Error ( "Error writing to user file" , err )
return c . SendStatus ( fiber . StatusInternalServerError )
}
log . Info (
2024-09-29 00:20:57 +05:30
"Registration request for " + formValues . Username + " has been submitted by the frontend and has been written to /var/publapi/users/" + formValues . Username + ".sh" ,
2023-07-11 02:53:29 +05:30
)
// send notification to user that their reg request was sent
2023-08-13 15:58:34 +05:30
// err = shoutrrr.Send(os.Getenv("PUBLAPI_EMAIL_SHOUTRRRURL")+email, "Hello "+username+",\nYour registration request has been sent.\nIt will take a maximum of 48 hours for the request to be processed.\nThank you for being part of the Project Segfault Pubnix.")
// if err != nil {
// log.Error("Error sending email to user", err)
// return c.SendStatus(fiber.StatusInternalServerError)
// }
2023-07-11 02:53:29 +05:30
// send notification to admins
2023-08-13 15:58:34 +05:30
shoutrrrUrl := os . Getenv ( "PUBLAPI_NOTIFY_SHOUTRRRURL" ) + os . Getenv ( "PUBLAPI_NOTIFY_ROOMS" )
2023-07-11 02:53:29 +05:30
err = shoutrrr . Send (
shoutrrrUrl ,
2024-09-29 00:20:57 +05:30
"New user signup! Please review /var/publapi/users/" + formValues . Username + ".sh to approve or deny the user. IP: " + formValues . IPAddress + " Email: " + formValues . Email ,
2023-07-11 02:53:29 +05:30
)
if err != nil {
2024-09-29 18:05:33 +05:30
log . Error ( "error sending notification to admins" , log . Any ( "err" , err ) )
2024-05-22 11:16:10 +05:30
//return c.SendStatus(fiber.StatusInternalServerError)
2023-07-11 02:53:29 +05:30
}
return c . JSON ( fiber . Map {
2024-09-29 00:20:57 +05:30
"username" : formValues . Username ,
2023-07-11 02:53:29 +05:30
"message" : "User created! Please allow us 24 hours or more to review your account." ,
"status" : c . Response ( ) . StatusCode ( ) ,
} )
}
2023-01-07 21:03:51 +05:30
}