first working server
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -41,8 +41,9 @@ go.work
|
|||||||
!README.md
|
!README.md
|
||||||
!LICENSE
|
!LICENSE
|
||||||
|
|
||||||
|
# all template files:
|
||||||
|
!*.template*
|
||||||
# !Makefile
|
# !Makefile
|
||||||
|
|
||||||
# ...even if they are in subdirectories
|
# ...even if they are in subdirectories
|
||||||
!*/
|
!*/
|
||||||
|
|
||||||
|
|||||||
13
configs/config.template.json
Normal file
13
configs/config.template.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"db":
|
||||||
|
{
|
||||||
|
"DBPath": "data/db.sqlite3"
|
||||||
|
},
|
||||||
|
"smtp": {
|
||||||
|
"server": "mail.server.com",
|
||||||
|
"user": "username",
|
||||||
|
"password": "password",
|
||||||
|
"port": 465,
|
||||||
|
"mailtype": "html"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
go.mod
4
go.mod
@@ -3,10 +3,8 @@ module GoMembership
|
|||||||
go 1.22.2
|
go 1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/mattn/go-sqlite3 v1.14.22
|
github.com/mattn/go-sqlite3 v1.14.22
|
||||||
golang.org/x/crypto v0.24.0
|
golang.org/x/crypto v0.24.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require filippo.io/edwards25519 v1.1.0 // indirect
|
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -1,7 +1,5 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
|
||||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
|
|||||||
@@ -1,38 +1,69 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"GoMembership/internal/utils"
|
||||||
|
"GoMembership/pkg/logger"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DatabaseConfig struct {
|
type DatabaseConfig struct {
|
||||||
DBPath string `json:"DBPath"`
|
DBPath string `json:"DBPath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type AuthenticationConfig struct {
|
||||||
DB DatabaseConfig `json:"db"`
|
JWTSecret string
|
||||||
|
CSRFSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
DB DatabaseConfig `json:"db"`
|
||||||
|
Auth AuthenticationConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
pConfig Config
|
||||||
|
once sync.Once
|
||||||
|
loaded bool
|
||||||
|
)
|
||||||
|
|
||||||
func LoadConfig() *Config {
|
func LoadConfig() *Config {
|
||||||
path, err := os.Getwd()
|
path, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not get working directory: %v", err)
|
logger.Error.Fatalf("could not get working directory: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
configFile, err := os.Open(filepath.Join(path, "configs", "config.json"))
|
configFile, err := os.Open(filepath.Join(path, "configs", "config.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not open config file: %v", err)
|
logger.Error.Fatalf("could not open config file: %v", err)
|
||||||
}
|
}
|
||||||
defer configFile.Close()
|
defer configFile.Close()
|
||||||
|
|
||||||
decoder := json.NewDecoder(configFile)
|
decoder := json.NewDecoder(configFile)
|
||||||
config := &Config{}
|
// pConfig = &Config{}
|
||||||
err = decoder.Decode(config)
|
err = decoder.Decode(&pConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not decode config file: %v", err)
|
logger.Error.Fatalf("could not decode config file: %v", err)
|
||||||
|
}
|
||||||
|
if !loaded {
|
||||||
|
once.Do(
|
||||||
|
func() {
|
||||||
|
csrfSecret, err := utils.GenerateRandomString(32)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error.Fatalf("could not generate CSRF secret: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jwtSecret, err := utils.GenerateRandomString(32)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error.Fatalf("could not generate JWT secret: %v", err)
|
||||||
|
}
|
||||||
|
pConfig.Auth.JWTSecret = jwtSecret
|
||||||
|
pConfig.Auth.CSRFSecret = csrfSecret
|
||||||
|
loaded = true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return config
|
return &pConfig
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
// "github.com/gorilla/mux"
|
// "github.com/gorilla/mux"
|
||||||
"net/http"
|
"net/http"
|
||||||
// "strconv"
|
// "strconv"
|
||||||
|
"GoMembership/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserController struct {
|
type UserController struct {
|
||||||
@@ -18,13 +19,17 @@ func NewUserController(service services.UserService) *UserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (uc *UserController) RegisterUser(w http.ResponseWriter, r *http.Request) {
|
func (uc *UserController) RegisterUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
logger.Info.Println("registering user")
|
||||||
var user models.User
|
var user models.User
|
||||||
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
logger.Error.Printf("Couldn't decode Userdata: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := uc.service.RegisterUser(&user); err != nil {
|
if err := uc.service.RegisterUser(&user); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
logger.Error.Printf("Couldn't register User: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
@@ -41,8 +46,8 @@ func (uc *UserController) RegisterUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
json.NewEncoder(w).Encode(user)
|
json.NewEncoder(w).Encode(user)
|
||||||
} */
|
}
|
||||||
|
*/
|
||||||
/* func (uc *UserController) GetUserID(w http.ResponseWriter, r *http.Request) {
|
/* func (uc *UserController) GetUserID(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id, err := strconv.Atoi(vars["id"])
|
id, err := strconv.Atoi(vars["id"])
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"GoMembership/internal/config"
|
"GoMembership/internal/config"
|
||||||
|
"GoMembership/pkg/logger"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
@@ -29,13 +29,21 @@ func initializeDB(dbPath string, schemaPath string) error {
|
|||||||
|
|
||||||
func Connect() *sql.DB {
|
func Connect() *sql.DB {
|
||||||
cfg := config.LoadConfig()
|
cfg := config.LoadConfig()
|
||||||
dsn := cfg.DB.DBPath
|
_, err := os.Stat(cfg.DB.DBPath)
|
||||||
db, err := sql.Open("sqlite3", dsn)
|
if os.IsNotExist(err) {
|
||||||
|
initErr := initializeDB(cfg.DB.DBPath, "internal/database/schema.sql")
|
||||||
|
if initErr != nil {
|
||||||
|
logger.Error.Fatalf("Couldn't create database: %v", initErr)
|
||||||
|
}
|
||||||
|
logger.Info.Println("Created new database")
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite3", cfg.DB.DBPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logger.Error.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := db.Ping(); err != nil {
|
if err := db.Ping(); err != nil {
|
||||||
log.Fatal(err)
|
logger.Error.Fatal(err)
|
||||||
}
|
}
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package middlewares
|
package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AuthMiddleware(next http.Handler) http.Handler {
|
func AuthMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(
|
||||||
// Authentication logic here
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
next.ServeHTTP(w, r)
|
token := r.Header.Get("Authorization")
|
||||||
})
|
if token != "your-secret-token" {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
114
internal/middlewares/csrf_middleware.go
Normal file
114
internal/middlewares/csrf_middleware.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"GoMembership/internal/config"
|
||||||
|
"GoMembership/internal/utils"
|
||||||
|
"GoMembership/pkg/logger"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateCSRFToken generates HMAC-signed CSRF token
|
||||||
|
func GenerateCSRFToken(sessionID string, secretKey string) string {
|
||||||
|
// Create message to be signed (e.g., combining sessionID with some random value)
|
||||||
|
randomString, err := utils.GenerateRandomString(8)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error.Fatalf("Could not create random string: %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
message := sessionID + "!" + randomString
|
||||||
|
|
||||||
|
// Create HMAC hash using SHA-256
|
||||||
|
h := hmac.New(sha256.New, []byte(secretKey))
|
||||||
|
h.Write([]byte(message))
|
||||||
|
signature := h.Sum(nil)
|
||||||
|
|
||||||
|
// Encode signature and message into a CSRF token
|
||||||
|
csrfToken := base64.StdEncoding.EncodeToString(signature) + "." + message
|
||||||
|
return csrfToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func ComputeHMAC(message string, secretKey string) []byte {
|
||||||
|
h := hmac.New(sha256.New, []byte(secretKey))
|
||||||
|
h.Write([]byte(message))
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSRFMiddleware verifies HMAC-signed CSRF token
|
||||||
|
func CSRFMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodGet || r.Method == http.MethodHead || r.Method == http.MethodOptions {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
csrfSecret := config.LoadConfig().Auth.CSRFSecret
|
||||||
|
// Retrieve CSRF token from request (e.g., from cookie, header, or form data)
|
||||||
|
csrfToken := r.Header.Get("X-CSRF-Token")
|
||||||
|
|
||||||
|
// Extract signature and message from CSRF token
|
||||||
|
parts := strings.SplitN(csrfToken, ".", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
http.Error(w, "Invalid CSRF token", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
receivedSignature := parts[0]
|
||||||
|
receivedMessage := parts[1]
|
||||||
|
|
||||||
|
// Compute HMAC using the received message and the CSRF secret key
|
||||||
|
computedSignature := ComputeHMAC(receivedMessage, csrfSecret)
|
||||||
|
|
||||||
|
// Compare computed HMAC with received signature
|
||||||
|
if !hmac.Equal([]byte(receivedSignature), computedSignature) {
|
||||||
|
http.Error(w, "CSRF Token validation failed", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSRF token is valid, proceed to the next handler
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateCSRFTokenHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Simulate getting session ID from authenticated session
|
||||||
|
sessionID := "exampleSessionID123"
|
||||||
|
|
||||||
|
// Generate HMAC-signed CSRF token
|
||||||
|
csrfToken := GenerateCSRFToken(sessionID, config.LoadConfig().Auth.CSRFSecret)
|
||||||
|
|
||||||
|
// Set CSRF token in a cookie (example)
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "csrf_token",
|
||||||
|
Value: csrfToken,
|
||||||
|
Path: "/",
|
||||||
|
HttpOnly: true,
|
||||||
|
Secure: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* func GenerateCSRFTokenHandler() http.Handler {
|
||||||
|
return http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
token, err := GenerateCSRFToken()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Could not generate CSRF token", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set CSRF token in cookie
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "csrf_token",
|
||||||
|
Value: token,
|
||||||
|
Path: "/",
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.Info.Printf("generated token: %v", token)
|
||||||
|
// Return CSRF token in response
|
||||||
|
w.Header().Set("X-CSRF-Token", token)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
} */
|
||||||
17
internal/middlewares/logger_middleware.go
Normal file
17
internal/middlewares/logger_middleware.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"GoMembership/pkg/logger"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoggerMiddleware logs each incoming HTTP request
|
||||||
|
func LoggerMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
start := time.Now()
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
logger.Info.Printf("%s %s %s", r.Method, r.RequestURI, time.Since(start))
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -9,7 +9,8 @@ type User struct {
|
|||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"-"`
|
||||||
|
Salt string `json:"-"`
|
||||||
IBAN string `json:"iban"`
|
IBAN string `json:"iban"`
|
||||||
BIC string `json:"bic"`
|
BIC string `json:"bic"`
|
||||||
MandateReference string `json:"mandate_reference"`
|
MandateReference string `json:"mandate_reference"`
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
type UserRepository interface {
|
type UserRepository interface {
|
||||||
CreateUser(user *models.User) error
|
CreateUser(user *models.User) error
|
||||||
FindUserByID(id int) (*models.User, error)
|
FindUserByID(id int) (*models.User, error)
|
||||||
// FindUserByEmail(email string) (*models.User, error)
|
FindUserByEmail(email string) (*models.User, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type userRepository struct {
|
type userRepository struct {
|
||||||
@@ -20,16 +20,29 @@ func NewUserRepository(db *sql.DB) UserRepository {
|
|||||||
return &userRepository{db}
|
return &userRepository{db}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *userRepository) CreateUser(user *models.User) error {
|
func (repo *userRepository) CreateUser(user *models.User) error {
|
||||||
query := "INSERT INTO users (first_name, last_name, email, password, iban, bic, mandate_reference, mandate_date_signed, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
query := "INSERT INTO users (first_name, last_name, email, password, salt, iban, bic, mandate_reference, mandate_date_signed, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
_, err := r.db.Exec(query, user.FirstName, user.LastName, user.Email, user.Password, user.CreatedAt, user.UpdatedAt)
|
_, err := repo.db.Exec(query, user.FirstName, user.LastName, user.Email, user.Password, user.Salt, user.IBAN, user.BIC, user.MandateReference, user.MandateDateSigned, user.CreatedAt, user.UpdatedAt)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *userRepository) FindUserByID(id int) (*models.User, error) {
|
func (repo *userRepository) FindUserByID(id int) (*models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
query := "SELECT id, first_name, last_name, email, iban, bic, mandate_reference FROM users WHERE id = ?"
|
query := "SELECT id, first_name, last_name, email, iban, bic, mandate_reference FROM users WHERE id = ?"
|
||||||
err := r.db.QueryRow(query, id).Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email, &user.IBAN, &user.BIC, &user.MandateReference)
|
err := repo.db.QueryRow(query, id).Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email, &user.IBAN, &user.BIC, &user.MandateReference)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, errors.ErrUserNotFound
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *userRepository) FindUserByEmail(email string) (*models.User, error) {
|
||||||
|
var user models.User
|
||||||
|
query := "SELECT id, first_name, last_name, email, iban, bic, mandate_reference FROM users WHERE email = ?"
|
||||||
|
err := repo.db.QueryRow(query, email).Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email, &user.IBAN, &user.BIC, &user.MandateReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, errors.ErrUserNotFound
|
return nil, errors.ErrUserNotFound
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"GoMembership/internal/controllers"
|
||||||
"GoMembership/internal/controllers"
|
// "GoMembership/internal/middlewares"
|
||||||
|
"GoMembership/pkg/logger"
|
||||||
|
// "net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterRoutes(router *mux.Router, userController *controllers.UserController) {
|
func RegisterRoutes(router *mux.Router, userController *controllers.UserController) {
|
||||||
router.HandleFunc("/register", userController.RegisterUser).Methods("POST")
|
logger.Info.Println("Registering /register route")
|
||||||
}
|
router.HandleFunc("/register", userController.RegisterUser).Methods("POST")
|
||||||
|
// router.HandleFunc("/login", userController.LoginUser).Methods("POST")
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "GoMembership/internal/config"
|
|
||||||
"GoMembership/internal/controllers"
|
"GoMembership/internal/controllers"
|
||||||
"GoMembership/internal/database"
|
"GoMembership/internal/database"
|
||||||
|
"GoMembership/internal/middlewares"
|
||||||
"GoMembership/internal/repositories"
|
"GoMembership/internal/repositories"
|
||||||
"GoMembership/internal/routes"
|
"GoMembership/internal/routes"
|
||||||
"GoMembership/internal/services"
|
"GoMembership/internal/services"
|
||||||
"log"
|
"GoMembership/pkg/logger"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run() {
|
func Run() {
|
||||||
// cfg := config.LoadConfig()
|
|
||||||
db := database.Connect()
|
db := database.Connect()
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
@@ -23,10 +22,20 @@ func Run() {
|
|||||||
userController := controllers.NewUserController(userService)
|
userController := controllers.NewUserController(userService)
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
routes.RegisterRoutes(router, userController)
|
// router.Handle("/csrf-token", middlewares.GenerateCSRFTokenHandler()).Methods("GET")
|
||||||
|
|
||||||
log.Println("Starting server on :8080")
|
// Apply CSRF middleware
|
||||||
|
// router.Use(middlewares.CSRFMiddleware)
|
||||||
|
router.Use(middlewares.LoggerMiddleware)
|
||||||
|
|
||||||
|
routes.RegisterRoutes(router, userController)
|
||||||
|
// create subrouter for teh authenticated area /account
|
||||||
|
// also pthprefix matches everything below /account
|
||||||
|
// accountRouter := router.PathPrefix("/account").Subrouter()
|
||||||
|
// accountRouter.Use(middlewares.AuthMiddleware)
|
||||||
|
|
||||||
|
logger.Info.Println("Starting server on :8080")
|
||||||
if err := http.ListenAndServe(":8080", router); err != nil {
|
if err := http.ListenAndServe(":8080", router); err != nil {
|
||||||
log.Fatalf("could not start server: %v", err)
|
logger.Error.Fatalf("could not start server: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,16 @@ package services
|
|||||||
import (
|
import (
|
||||||
"GoMembership/internal/models"
|
"GoMembership/internal/models"
|
||||||
"GoMembership/internal/repositories"
|
"GoMembership/internal/repositories"
|
||||||
|
// "GoMembership/pkg/errors"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserService interface {
|
type UserService interface {
|
||||||
RegisterUser(user *models.User) error
|
RegisterUser(user *models.User) error
|
||||||
|
// AuthenticateUser(email, password string) (*models.User, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type userService struct {
|
type userService struct {
|
||||||
@@ -19,8 +23,14 @@ func NewUserService(repo repositories.UserRepository) UserService {
|
|||||||
return &userService{repo}
|
return &userService{repo}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *userService) RegisterUser(user *models.User) error {
|
func (service *userService) RegisterUser(user *models.User) error {
|
||||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
salt := make([]byte, 16)
|
||||||
|
if _, err := rand.Read(salt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user.Salt = base64.StdEncoding.EncodeToString(salt)
|
||||||
|
|
||||||
|
hashedPassword, err := HashPassword(user.Password, user.Salt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -28,5 +38,38 @@ func (s *userService) RegisterUser(user *models.User) error {
|
|||||||
user.CreatedAt = time.Now()
|
user.CreatedAt = time.Now()
|
||||||
user.UpdatedAt = time.Now()
|
user.UpdatedAt = time.Now()
|
||||||
user.MandateDateSigned = time.Now()
|
user.MandateDateSigned = time.Now()
|
||||||
return s.repo.CreateUser(user)
|
return service.repo.CreateUser(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HashPassword(password string, salt string) (string, error) {
|
||||||
|
saltedPassword := password + salt
|
||||||
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(saltedPassword), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(hashedPassword), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* func (s *userService) AuthenticateUser(email, password string) (*models.User, error) {
|
||||||
|
user, err := s.repo.FindUserByEmail(email)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if !verifyPassword(password, user.Password, user.Salt) {
|
||||||
|
return nil, errors.ErrInvalidCredentials
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/* func verifyPassword(password string, storedPassword string, salt string) bool {
|
||||||
|
|
||||||
|
saltedPassword := password + salt
|
||||||
|
decodedStoredPassword, err := base64.StdEncoding.DecodeString(storedPassword)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
err = bcrypt.CompareHashAndPassword([]byte(decodedStoredPassword), []byte(saltedPassword))
|
||||||
|
return err == nil
|
||||||
|
} */
|
||||||
|
|||||||
15
internal/utils/crypto.go
Normal file
15
internal/utils/crypto.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateRandomString(length int) (string, error) {
|
||||||
|
bytes := make([]byte, length)
|
||||||
|
_, err := rand.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.URLEncoding.EncodeToString(bytes), nil
|
||||||
|
}
|
||||||
@@ -3,8 +3,7 @@ package errors
|
|||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrUserNotFound = errors.New("user not found")
|
ErrUserNotFound = errors.New("user not found")
|
||||||
ErrInvalidEmail = errors.New("invalid email")
|
ErrInvalidEmail = errors.New("invalid email")
|
||||||
// Add other custom errors here
|
ErrInvalidCredentials = errors.New("invalid credentials: unauthorized")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Info *log.Logger
|
Info *log.Logger
|
||||||
Warning *log.Logger
|
Warning *log.Logger
|
||||||
Error *log.Logger
|
Error *log.Logger
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
file, err := os.OpenFile("gomember.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
Info = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
|
Info = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||||
Warning = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
|
Warning = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||||
Error = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
|
Error = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
module git.stoelti.land/Alex/GoMembership
|
|
||||||
|
|
||||||
go 1.22.4
|
|
||||||
57
src/main.go
57
src/main.go
@@ -1,57 +0,0 @@
|
|||||||
// main.go
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
/* This would match routes like the following:
|
|
||||||
/sum/3/5
|
|
||||||
/product/6/23
|
|
||||||
...
|
|
||||||
*/
|
|
||||||
beego.Router("/:operation/:num1:int/:num2:int", &mainController{})
|
|
||||||
beego.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
type mainController struct {
|
|
||||||
beego.Controller
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (c *mainController) Get() {
|
|
||||||
|
|
||||||
//Obtain the values of the route parameters defined in the route above
|
|
||||||
operation := c.Ctx.Input.Param(":operation")
|
|
||||||
num1, _ := strconv.Atoi(c.Ctx.Input.Param(":num1"))
|
|
||||||
num2, _ := strconv.Atoi(c.Ctx.Input.Param(":num2"))
|
|
||||||
|
|
||||||
//Set the values for use in the template
|
|
||||||
c.Data["operation"] = operation
|
|
||||||
c.Data["num1"] = num1
|
|
||||||
c.Data["num2"] = num2
|
|
||||||
c.TplName = "result.html"
|
|
||||||
|
|
||||||
// Perform the calculation depending on the 'operation' route parameter
|
|
||||||
switch operation {
|
|
||||||
case "sum":
|
|
||||||
c.Data["result"] = add(num1, num2)
|
|
||||||
case "product":
|
|
||||||
c.Data["result"] = multiply(num1, num2)
|
|
||||||
default:
|
|
||||||
c.TplName = "invalid-route.html"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func add(n1, n2 int) int {
|
|
||||||
return n1 + n2
|
|
||||||
}
|
|
||||||
|
|
||||||
func multiply(n1, n2 int) int {
|
|
||||||
return n1 * n2
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user