Compare commits

...

7 Commits

Author SHA1 Message Date
$(pass /github/name)
f99ff57275 minor 2024-09-07 08:56:33 +02:00
$(pass /github/name)
c36af961f3 fix auth routes 2024-09-07 08:56:26 +02:00
$(pass /github/name)
b3dc134c8c add admin user creation 2024-09-07 08:56:15 +02:00
$(pass /github/name)
066419e546 fix: user model json handling; user_controller_test debug logging, user_controller 2024-09-07 08:55:39 +02:00
$(pass /github/name)
ff7c83671f fix: auth && auth_test 2024-09-07 08:53:57 +02:00
$(pass /github/name)
ef5e771998 chg: cors allowOrigins now configurable 2024-09-07 08:52:48 +02:00
$(pass /github/name)
8f3d73af90 add: AllowOrigins config && changed config format 2024-09-07 08:51:58 +02:00
15 changed files with 267 additions and 133 deletions

View File

@@ -18,7 +18,7 @@ func main() {
config.LoadConfig() config.LoadConfig()
err := database.Open(config.DB.Path) err := database.Open(config.DB.Path, config.Recipients.AdminEmail)
if err != nil { if err != nil {
logger.Error.Fatalf("Couldn't init database: %v", err) logger.Error.Fatalf("Couldn't init database: %v", err)
} }

View File

@@ -1,6 +1,9 @@
{ {
"WebsiteTitle": "My Carsharing Site", "site": {
"BaseURL": "https://domain.de", "WebsiteTitle": "My Carsharing Site",
"BaseUrl": "https://domain.de",
"AllowOrigins": "https://domain.de"
},
"Environment": "dev", "Environment": "dev",
"db": { "db": {
"Path": "data/db.sqlite3" "Path": "data/db.sqlite3"

View File

@@ -23,6 +23,11 @@ type DatabaseConfig struct {
Path string `json:"Path" default:"data/db.sqlite3" envconfig:"DB_PATH"` Path string `json:"Path" default:"data/db.sqlite3" envconfig:"DB_PATH"`
} }
type SiteConfig struct {
AllowOrigins string `json:"AllowOrigins" envconfig:"ALLOW_ORIGINS"`
WebsiteTitle string `json:"WebsiteTitle" envconfig:"WEBSITE_TITLE"`
BaseURL string `json:"BaseUrl" envconfig:"BASE_URL"`
}
type AuthenticationConfig struct { type AuthenticationConfig struct {
JWTSecret string JWTSecret string
CSRFSecret string CSRFSecret string
@@ -57,11 +62,10 @@ type SecurityConfig struct {
} }
type Config struct { type Config struct {
Auth AuthenticationConfig `json:"auth"` Auth AuthenticationConfig `json:"auth"`
Site SiteConfig `json:"site"`
Templates TemplateConfig `json:"templates"` Templates TemplateConfig `json:"templates"`
Recipients RecipientsConfig `json:"recipients"` Recipients RecipientsConfig `json:"recipients"`
ConfigFilePath string `json:"config_file_path" envconfig:"CONFIG_FILE_PATH"` ConfigFilePath string `json:"config_file_path" envconfig:"CONFIG_FILE_PATH"`
WebsiteTitle string `json:"WebsiteTitle" envconfig:"WEBSITE_TITLE"`
BaseURL string `json:"BaseUrl" envconfig:"BASE_URL"`
Env string `json:"Environment" default:"development" envconfig:"ENV"` Env string `json:"Environment" default:"development" envconfig:"ENV"`
DB DatabaseConfig `json:"db"` DB DatabaseConfig `json:"db"`
SMTP SMTPConfig `json:"smtp"` SMTP SMTPConfig `json:"smtp"`
@@ -69,17 +73,16 @@ type Config struct {
} }
var ( var (
BaseURL string Site SiteConfig
WebsiteTitle string CFGPath string
CFGPath string CFG Config
CFG Config Auth AuthenticationConfig
Auth AuthenticationConfig DB DatabaseConfig
DB DatabaseConfig Templates TemplateConfig
Templates TemplateConfig SMTP SMTPConfig
SMTP SMTPConfig Recipients RecipientsConfig
Recipients RecipientsConfig Env string
Env string Security SecurityConfig
Security SecurityConfig
) )
var environmentOptions map[string]bool = map[string]bool{ var environmentOptions map[string]bool = map[string]bool{
"development": true, "development": true,
@@ -115,11 +118,10 @@ func LoadConfig() {
DB = CFG.DB DB = CFG.DB
Templates = CFG.Templates Templates = CFG.Templates
SMTP = CFG.SMTP SMTP = CFG.SMTP
BaseURL = CFG.BaseURL
Recipients = CFG.Recipients Recipients = CFG.Recipients
Security = CFG.Security Security = CFG.Security
Env = CFG.Env Env = CFG.Env
WebsiteTitle = CFG.WebsiteTitle Site = CFG.Site
logger.Info.Printf("Config loaded: %#v", CFG) logger.Info.Printf("Config loaded: %#v", CFG)
} }

View File

@@ -44,9 +44,6 @@ var (
func TestSuite(t *testing.T) { func TestSuite(t *testing.T) {
_ = deleteTestDB("test.db") _ = deleteTestDB("test.db")
if err := database.Open("test.db"); err != nil {
log.Fatalf("Failed to create DB: %#v", err)
}
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
@@ -77,6 +74,9 @@ func TestSuite(t *testing.T) {
log.Fatalf("Error setting environment variable: %v", err) log.Fatalf("Error setting environment variable: %v", err)
} }
config.LoadConfig() config.LoadConfig()
if err := database.Open("test.db", config.Recipients.AdminEmail); err != nil {
log.Fatalf("Failed to create DB: %#v", err)
}
utils.SMTPStart(Host, Port) utils.SMTPStart(Host, Port)
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password) emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password)
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{} var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}

View File

@@ -24,17 +24,19 @@ type UserController struct {
} }
type RegistrationData struct { type RegistrationData struct {
User models.User `json:"user"` User models.User `json:"user"`
Password string `json:"password"`
} }
func (uc *UserController) CurrentUserHandler(c *gin.Context) { func (uc *UserController) CurrentUserHandler(c *gin.Context) {
userID, err := middlewares.GetUserIDFromContext(c) userIDString, ok := c.Get("user_id")
if err != nil { if !ok || userIDString == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Failed to authenticate user"}) logger.Error.Printf("Error getting user_id from header")
c.Abort()
return
} }
user, err := uc.Service.GetUserByID(userID) userID := userIDString.(float64)
logger.Error.Printf("UserIDINt64: %v", userID)
logger.Error.Printf("c.Get Value: %v", userIDString)
user, err := uc.Service.GetUserByID(int64(userID))
if err != nil { if err != nil {
logger.Error.Printf("Error retrieving valid user: %v", err) logger.Error.Printf("Error retrieving valid user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving user."}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving user."})
@@ -124,6 +126,7 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
// logger.Info.Printf("REGISTERING user: %#v", regData.User) // logger.Info.Printf("REGISTERING user: %#v", regData.User)
regData.User.RoleID = constants.Roles.Member regData.User.RoleID = constants.Roles.Member
regData.User.Password = regData.Password
// Register User // Register User
id, token, err := uc.Service.RegisterUser(&regData.User) id, token, err := uc.Service.RegisterUser(&regData.User)

View File

@@ -18,11 +18,17 @@ import (
"GoMembership/internal/config" "GoMembership/internal/config"
"GoMembership/internal/constants" "GoMembership/internal/constants"
"GoMembership/internal/middlewares"
"GoMembership/internal/models" "GoMembership/internal/models"
"GoMembership/internal/utils" "GoMembership/internal/utils"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
) )
type loginInput struct {
Email string `json:"email"`
Password string `json:"password"`
}
type RegisterUserTest struct { type RegisterUserTest struct {
WantDBData map[string]interface{} WantDBData map[string]interface{}
Name string Name string
@@ -61,11 +67,13 @@ func testUserController(t *testing.T) {
} }
}) })
} }
testLoginUser(t) testCurrentUserHandler(t)
} }
func testLoginUser(t *testing.T) { func testLoginUser(t *testing.T) (string, http.Cookie) {
// This test should run after the user registration test // This test should run after the user registration test
var loginCookie http.Cookie
var loginInput loginInput
t.Run("LoginUser", func(t *testing.T) { t.Run("LoginUser", func(t *testing.T) {
// Test cases // Test cases
tests := []struct { tests := []struct {
@@ -104,6 +112,9 @@ func testLoginUser(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
logger.Error.Print("==============================================================")
logger.Error.Printf("Testing : %v", tt.name)
logger.Error.Print("==============================================================")
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Setup // Setup
c, w, _ := GetMockedJSONContext([]byte(tt.input), "/login") c, w, _ := GetMockedJSONContext([]byte(tt.input), "/login")
@@ -122,13 +133,113 @@ func testLoginUser(t *testing.T) {
logger.Info.Printf("Response: %#v", response) logger.Info.Printf("Response: %#v", response)
assert.Contains(t, response, "set-token") assert.Contains(t, response, "set-token")
assert.NotEmpty(t, response["set-token"]) assert.NotEmpty(t, response["set-token"])
for _, cookie := range w.Result().Cookies() {
if cookie.Name == "jwt" {
loginCookie = *cookie
err = json.Unmarshal([]byte(tt.input), &loginInput)
assert.NoError(t, err, "Failed to unmarshal input JSON")
break
}
}
assert.NotEmpty(t, loginCookie)
} else { } else {
assert.NotContains(t, response, "set-token") assert.NotContains(t, response, "set-token")
} }
}) })
} }
}) })
return loginInput.Email, loginCookie
} }
func testCurrentUserHandler(t *testing.T) {
loginEmail, loginCookie := testLoginUser(t)
// This test should run after the user login test
invalidCookie := http.Cookie{
Name: "jwt",
Value: "invalid.token.here",
}
tests := []struct {
name string
setupCookie func(*http.Request)
expectedUserMail string
expectedStatus int
}{
{
name: "With valid cookie",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
expectedUserMail: loginEmail,
expectedStatus: http.StatusOK,
},
{
name: "Without cookie",
setupCookie: func(req *http.Request) {},
expectedStatus: http.StatusUnauthorized,
},
{
name: "With invalid cookie",
setupCookie: func(req *http.Request) {
req.AddCookie(&invalidCookie)
},
expectedStatus: http.StatusUnauthorized,
},
}
for _, tt := range tests {
logger.Error.Print("==============================================================")
logger.Error.Printf("Testing : %v", tt.name)
logger.Error.Print("==============================================================")
if tt.expectedStatus == http.StatusOK {
time.Sleep(time.Second) // Small delay to ensure different timestamps to get a different JWT token
}
t.Run(tt.name, func(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(middlewares.AuthMiddleware())
router.GET("/current-user", Uc.CurrentUserHandler)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/current-user", nil)
tt.setupCookie(req)
router.ServeHTTP(w, req)
assert.Equal(t, tt.expectedStatus, w.Code)
if tt.expectedStatus == http.StatusOK {
var response models.User
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, tt.expectedUserMail, response.Email)
var newCookie *http.Cookie
for _, cookie := range w.Result().Cookies() {
if cookie.Name == "jwt" {
newCookie = cookie
break
}
}
assert.NotNil(t, newCookie, "Cookie should be renewed")
assert.NotEqual(t, loginCookie.Value, newCookie.Value, "Cookie value should be different")
assert.True(t, newCookie.MaxAge > 0, "New cookie should not be expired")
} else {
// For unauthorized requests, check for an error message
var errorResponse map[string]string
err := json.Unmarshal(w.Body.Bytes(), &errorResponse)
assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
assert.NotEmpty(t, errorResponse["error"])
}
})
}
}
func validateUser(assert bool, wantDBData map[string]interface{}) error { func validateUser(assert bool, wantDBData map[string]interface{}) error {
users, err := Uc.Service.GetUsers(wantDBData) users, err := Uc.Service.GetUsers(wantDBData)
if err != nil { if err != nil {
@@ -195,14 +306,14 @@ func checkWelcomeMail(message *utils.Email, user *models.User) error {
if !strings.Contains(message.Body, fmt.Sprintf("Mitgliedsnummer</strong>: %v", user.Membership.ID)) { if !strings.Contains(message.Body, fmt.Sprintf("Mitgliedsnummer</strong>: %v", user.Membership.ID)) {
return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID) return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID)
} }
if !strings.Contains(message.Body, config.BaseURL) { if !strings.Contains(message.Body, config.Site.BaseURL) {
return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL) return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.Site.BaseURL)
} }
if !strings.Contains(message.Body, config.BaseURL+config.Templates.LogoURI) { if !strings.Contains(message.Body, config.Site.BaseURL+config.Templates.LogoURI) {
return fmt.Errorf("Logo Url (%v) has not been rendered in registration mail.", config.BaseURL+config.WebsiteTitle) return fmt.Errorf("Logo Url (%v) has not been rendered in registration mail.", config.Site.BaseURL+config.Site.WebsiteTitle)
} }
if !strings.Contains(message.Body, config.WebsiteTitle) { if !strings.Contains(message.Body, config.Site.WebsiteTitle) {
return fmt.Errorf("Website title (%v) has not been rendered in registration mail.", config.WebsiteTitle) return fmt.Errorf("Website title (%v) has not been rendered in registration mail.", config.Site.WebsiteTitle)
} }
return nil return nil
} }
@@ -249,8 +360,8 @@ func checkRegistrationMail(message *utils.Email, user *models.User) error {
if !strings.Contains(message.Body, user.BankAccount.IBAN) { if !strings.Contains(message.Body, user.BankAccount.IBAN) {
return fmt.Errorf("Users IBAN(%v) has not been rendered in registration mail.", user.BankAccount.IBAN) return fmt.Errorf("Users IBAN(%v) has not been rendered in registration mail.", user.BankAccount.IBAN)
} }
if !strings.Contains(message.Body, config.BaseURL) { if !strings.Contains(message.Body, config.Site.BaseURL) {
return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL) return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.Site.BaseURL)
} }
return nil return nil
} }
@@ -267,8 +378,8 @@ func checkVerificationMail(message *utils.Email, user *models.User) error {
return fmt.Errorf("Users Verification link token(%v) has not been rendered in email verification mail. %v", user.Verification.VerificationToken, verificationURL) return fmt.Errorf("Users Verification link token(%v) has not been rendered in email verification mail. %v", user.Verification.VerificationToken, verificationURL)
} }
if !strings.Contains(message.Body, config.BaseURL) { if !strings.Contains(message.Body, config.Site.BaseURL) {
return fmt.Errorf("Base Url (%v) has not been rendered in email verification mail.", config.BaseURL) return fmt.Errorf("Base Url (%v) has not been rendered in email verification mail.", config.Site.BaseURL)
} }
// open the provided link: // open the provided link:
if err := verifyMail(verificationURL); err != nil { if err := verifyMail(verificationURL); err != nil {
@@ -349,7 +460,7 @@ func getBaseUser() models.User {
func customizeInput(customize func(models.User) models.User) *RegistrationData { func customizeInput(customize func(models.User) models.User) *RegistrationData {
user := getBaseUser() user := getBaseUser()
user = customize(user) // Apply the customization user = customize(user) // Apply the customization
return &RegistrationData{User: user} return &RegistrationData{User: user, Password: user.Password}
} }
func getTestUsers() []RegisterUserTest { func getTestUsers() []RegisterUserTest {

View File

@@ -5,7 +5,7 @@ import (
"GoMembership/internal/models" "GoMembership/internal/models"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/base64"
"time" "time"
"github.com/alexedwards/argon2id" "github.com/alexedwards/argon2id"
@@ -15,7 +15,7 @@ import (
var DB *gorm.DB var DB *gorm.DB
func Open(dbPath string) error { func Open(dbPath string, adminMail string) error {
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil { if err != nil {
@@ -34,12 +34,60 @@ func Open(dbPath string) error {
DB = db DB = db
logger.Info.Print("Opened DB") logger.Info.Print("Opened DB")
if err := seedDatabase(); err != nil {
return err var count int64
db.Model(&models.User{}).Count(&count)
if count == 0 {
admin, err := seedAdmin(adminMail)
if err != nil {
return err
}
result := db.Create(&admin)
if result.Error != nil {
return result.Error
}
} }
return nil return nil
} }
// TODO: Landing page to create an admin
func seedAdmin(userMail string) (*models.User, error) {
passwordBytes := make([]byte, 12)
_, err := rand.Read(passwordBytes)
if err != nil {
return nil, err
}
// Encode into a URL-safe base64 string
password, err := base64.URLEncoding.EncodeToString(passwordBytes)[:12], nil
if err != nil {
return nil, err
}
hash, err := argon2id.CreateHash(password, argon2id.DefaultParams)
if err != nil {
return nil, err
}
logger.Error.Print("==============================================================")
logger.Error.Printf("Admin Email: %v", userMail)
logger.Error.Printf("Admin Password: %v", password)
logger.Error.Print("==============================================================")
return &models.User{
FirstName: "ad",
LastName: "min",
DateOfBirth: time.Now(),
Password: hash,
Address: "Downhill 4",
ZipCode: "9999",
City: "TechTown",
Email: userMail,
Status: constants.ActiveStatus,
RoleID: constants.Roles.Editor,
}, nil
}
func Close() error { func Close() error {
logger.Info.Print("Closing DB") logger.Info.Print("Closing DB")
db, err := DB.DB() db, err := DB.DB()
@@ -48,42 +96,3 @@ func Close() error {
} }
return db.Close() return db.Close()
} }
func seedDatabase() error {
var count int64
DB.Model(&models.User{}).Count(&count)
if count == 0 {
bytes := make([]byte, 12)
_, err := rand.Read(bytes)
if err != nil {
return err
}
password := hex.EncodeToString(bytes)
hash, err := argon2id.CreateHash(password, argon2id.DefaultParams)
if err != nil {
return err
}
admin := models.User{
FirstName: "ad",
LastName: "min",
DateOfBirth: time.Now(),
Password: hash,
Address: "Downhill 4",
ZipCode: "9999",
City: "TechTown",
Status: constants.ActiveStatus,
RoleID: constants.Roles.Editor,
}
result := DB.Create(&admin)
if result.Error != nil {
return result.Error
}
logger.Error.Print("==============================================================")
logger.Error.Printf("Admin Password: %v", password)
logger.Error.Print("==============================================================")
}
return nil
}

View File

@@ -28,6 +28,9 @@ func GenerateToken(userID int64) (string, error) {
} }
func verifyToken(tokenString string) (*jwt.Token, error) { func verifyToken(tokenString string) (*jwt.Token, error) {
if tokenString == "" {
return nil, fmt.Errorf("Authorization token is required")
}
token, err := jwtParser.Parse(tokenString, func(_ *jwt.Token) (interface{}, error) { token, err := jwtParser.Parse(tokenString, func(_ *jwt.Token) (interface{}, error) {
return jwtKey, nil return jwtKey, nil
}) })
@@ -36,55 +39,53 @@ func verifyToken(tokenString string) (*jwt.Token, error) {
return nil, err return nil, err
} }
return token, nil if !token.Valid {
} return nil, fmt.Errorf("invalid token")
func GetUserIDFromContext(c *gin.Context) (int64, error) {
tokenString, err := c.Cookie("jwt")
if err != nil {
logger.Error.Printf("Error getting cookie: %v\n", err)
return 0, err
}
if tokenString == "" {
logger.Error.Printf("Token is empty: %v\n", err)
return 0, fmt.Errorf("Authorization token is required")
}
token, err := verifyToken(tokenString)
if err != nil || !token.Valid {
logger.Error.Printf("Token is invalid: %v\n", err)
return 0, fmt.Errorf("Token not valid!")
} }
claims, ok := token.Claims.(jwt.MapClaims) claims, ok := token.Claims.(jwt.MapClaims)
logger.Error.Printf("claims userid: %v", claims["user_id"].(float64))
if !ok { if !ok {
logger.Error.Printf("Invalid Token claims") return nil, fmt.Errorf("invalid token claims")
return 0, fmt.Errorf("Invalid token claims")
} }
userID, ok := claims["user_id"].(float64)
exp, ok := claims["exp"].(float64)
if !ok {
return nil, fmt.Errorf("invalid expiration claim")
}
userID, ok := claims["user_id"].(float64)
if !ok { if !ok {
logger.Error.Printf("Invalid user ID: %v", userID) logger.Error.Printf("Invalid user ID: %v", userID)
return 0, fmt.Errorf("Invalid user ID") return nil, fmt.Errorf("Invalid user ID")
} }
return int64(userID), nil if time.Now().Unix() > int64(exp) {
return nil, fmt.Errorf("token expired")
}
return token, nil
} }
func AuthMiddleware() gin.HandlerFunc { func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
tokenString, err := c.Cookie("jwt")
userID, err := GetUserIDFromContext(c)
if err != nil { if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) logger.Error.Printf("No Auth token: %v\n", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "No Auth token"})
c.Abort() c.Abort()
return return
} }
token, err := verifyToken(tokenString)
if err != nil {
logger.Error.Printf("Token is invalid: %v\n", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Auth token invalid"})
c.Abort()
return
}
claims, _ := token.Claims.(jwt.MapClaims)
userID, _ := claims["user_id"].(float64)
// Generate a new token // Generate a new token
newToken, err := GenerateToken(int64(userID)) newToken, err := GenerateToken(int64(userID))
if err != nil { if err != nil {

View File

@@ -111,6 +111,9 @@ func TestAuthMiddleware(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
logger.Error.Print("==============================================================")
logger.Error.Printf("Testing : %v", tt.name)
logger.Error.Print("==============================================================")
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Setup // Setup
r := gin.New() r := gin.New()

View File

@@ -11,8 +11,8 @@ import (
func CORSMiddleware() gin.HandlerFunc { func CORSMiddleware() gin.HandlerFunc {
logger.Info.Print("Applying CORS") logger.Info.Print("Applying CORS")
return cors.New(cors.Config{ return cors.New(cors.Config{
AllowOrigins: []string{config.BaseURL, "http://localhost:8080"}, // Add your frontend URL(s) AllowOrigins: []string{config.Site.AllowOrigins},
AllowMethods: []string{"GET", "POST"}, // "PUT", "PATCH", "DELETE", "OPTIONS"}, AllowMethods: []string{"GET", "POST"}, // "PUT", "PATCH", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With"}, AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With"},
// ExposeHeaders: []string{"Content-Length"}, // ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true, AllowCredentials: true,

View File

@@ -69,10 +69,10 @@ func TestCORSMiddleware(t *testing.T) {
}{ }{
{ {
name: "Allowed origin", name: "Allowed origin",
origin: config.BaseURL, origin: config.Site.AllowOrigins,
expectedStatus: http.StatusOK, expectedStatus: http.StatusOK,
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
"Access-Control-Allow-Origin": config.BaseURL, "Access-Control-Allow-Origin": config.Site.AllowOrigins,
"Content-Type": "text/plain; charset=utf-8", "Content-Type": "text/plain; charset=utf-8",
"Access-Control-Allow-Credentials": "true", "Access-Control-Allow-Credentials": "true",
}, },

View File

@@ -16,7 +16,7 @@ type User struct {
Phone string `json:"phone" validate:"omitempty,omitnil"` Phone string `json:"phone" validate:"omitempty,omitnil"`
Notes *string `json:"notes"` Notes *string `json:"notes"`
FirstName string `gorm:"not null" json:"first_name" validate:"required"` FirstName string `gorm:"not null" json:"first_name" validate:"required"`
Password string `json:"password" required_unless=RoleID 0` Password string `json:"-" required_unless=RoleID 0`
Email string `gorm:"unique;not null" json:"email" validate:"required,email"` Email string `gorm:"unique;not null" json:"email" validate:"required,email"`
LastName string `gorm:"not null" json:"last_name" validate:"required"` LastName string `gorm:"not null" json:"last_name" validate:"required"`
ProfilePicture string `json:"profile_picture" validate:"omitempty,omitnil,image"` ProfilePicture string `json:"profile_picture" validate:"omitempty,omitnil,image"`
@@ -27,7 +27,7 @@ type User struct {
BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"bank_account"` BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"bank_account"`
Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"membership"` Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"membership"`
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey" json:"id"`
PaymentStatus int8 `json:"payment_status"` PaymentStatus int8 `json:"payment_status"`
Status int8 `json:"status"` Status int8 `json:"status"`
RoleID int8 `json:"role_id"` RoleID int8 `json:"role_id"`

View File

@@ -12,6 +12,7 @@ func RegisterRoutes(router *gin.Engine, userController *controllers.UserControll
router.POST("/users/register", userController.RegisterUser) router.POST("/users/register", userController.RegisterUser)
router.POST("/users/contact", contactController.RelayContactRequest) router.POST("/users/contact", contactController.RelayContactRequest)
router.POST("/users/login", userController.LoginUser)
router.POST("/csp-report", middlewares.CSPReportHandling) router.POST("/csp-report", middlewares.CSPReportHandling)
// create subrouter for teh authenticated area /account // create subrouter for teh authenticated area /account
@@ -26,8 +27,8 @@ func RegisterRoutes(router *gin.Engine, userController *controllers.UserControll
apiRouter.Use(middlewares.APIKeyMiddleware()) apiRouter.Use(middlewares.APIKeyMiddleware())
authRouter := router.Group("/users/backend") authRouter := router.Group("/users/backend")
{
router.POST("/currentUser", userController.CurrentUserHandler)
}
authRouter.Use(middlewares.AuthMiddleware()) authRouter.Use(middlewares.AuthMiddleware())
{
authRouter.POST("/currentUser", userController.CurrentUserHandler)
}
} }

View File

@@ -70,7 +70,7 @@ func (s *EmailService) SendVerificationEmail(user *models.User, token *string) e
FirstName: user.FirstName, FirstName: user.FirstName,
LastName: user.LastName, LastName: user.LastName,
Token: *token, Token: *token,
BASEURL: config.BaseURL, BASEURL: config.Site.BaseURL,
} }
subject := constants.MailVerificationSubject subject := constants.MailVerificationSubject
@@ -102,8 +102,8 @@ func (s *EmailService) SendWelcomeEmail(user *models.User) error {
MembershipID: user.Membership.ID, MembershipID: user.Membership.ID,
MembershipFee: float32(user.Membership.SubscriptionModel.MonthlyFee), MembershipFee: float32(user.Membership.SubscriptionModel.MonthlyFee),
RentalFee: float32(user.Membership.SubscriptionModel.HourlyRate), RentalFee: float32(user.Membership.SubscriptionModel.HourlyRate),
BASEURL: config.BaseURL, BASEURL: config.Site.BaseURL,
WebsiteTitle: config.WebsiteTitle, WebsiteTitle: config.Site.WebsiteTitle,
Logo: config.Templates.LogoURI, Logo: config.Templates.LogoURI,
} }
@@ -151,9 +151,9 @@ func (s *EmailService) SendRegistrationNotification(user *models.User) error {
Email: user.Email, Email: user.Email,
Phone: user.Phone, Phone: user.Phone,
IBAN: user.BankAccount.IBAN, IBAN: user.BankAccount.IBAN,
BASEURL: config.BaseURL, BASEURL: config.Site.BaseURL,
Logo: config.Templates.LogoURI, Logo: config.Templates.LogoURI,
WebsiteTitle: config.WebsiteTitle, WebsiteTitle: config.Site.WebsiteTitle,
} }
subject := constants.MailRegistrationSubject subject := constants.MailRegistrationSubject
@@ -175,9 +175,9 @@ func (s *EmailService) RelayContactFormMessage(sender string, name string, messa
}{ }{
Message: message, Message: message,
Name: name, Name: name,
BASEURL: config.BaseURL, BASEURL: config.Site.BaseURL,
Logo: config.Templates.LogoURI, Logo: config.Templates.LogoURI,
WebsiteTitle: config.WebsiteTitle, WebsiteTitle: config.Site.WebsiteTitle,
} }
subject := constants.MailContactSubject subject := constants.MailContactSubject
body, err := ParseTemplate("mail_contact_form.tmpl", data) body, err := ParseTemplate("mail_contact_form.tmpl", data)

View File

@@ -6,12 +6,13 @@ import (
"GoMembership/internal/database" "GoMembership/internal/database"
"GoMembership/internal/models" "GoMembership/internal/models"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
"github.com/go-playground/validator/v10"
"github.com/jbub/banking/iban"
"github.com/jbub/banking/swift"
"reflect" "reflect"
"slices" "slices"
"time" "time"
"github.com/go-playground/validator/v10"
"github.com/jbub/banking/iban"
"github.com/jbub/banking/swift"
) )
// //