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 @@
{ {
"site": {
"WebsiteTitle": "My Carsharing Site", "WebsiteTitle": "My Carsharing Site",
"BaseURL": "https://domain.de", "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,8 +73,7 @@ type Config struct {
} }
var ( var (
BaseURL string Site SiteConfig
WebsiteTitle string
CFGPath string CFGPath string
CFG Config CFG Config
Auth AuthenticationConfig Auth AuthenticationConfig
@@ -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

@@ -25,16 +25,18 @@ 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 {
var count int64
db.Model(&models.User{}).Count(&count)
if count == 0 {
admin, err := seedAdmin(adminMail)
if err != nil {
return err 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,7 +11,7 @@ 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"},

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"
) )
// //