[PD2] Gateway login, logout and auth middleware done.

Co-authored-by: tsousa111 <tiagao2001@hotmail.com>
This commit is contained in:
Afonso Franco 2024-05-30 00:54:19 +01:00
parent aa90bfddce
commit 69559f41ca
Signed by: afonso
SSH key fingerprint: SHA256:PQTRDHPH3yALEGtHXnXBp3Orfcn21pK20t0tS1kHg54
7 changed files with 277 additions and 28 deletions

View file

@ -0,0 +1,52 @@
package gateway
import(
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
type DataStore struct {
db *sql.DB
}
func OpenDB() (DataStore, error) {
db, err := sql.Open("sqlite3", "gateway.db")
if err != nil {
return DataStore{}, err
}
ds := DataStore{db: db}
err = ds.CreateTables()
if err != nil {
return DataStore{}, err
}
return ds, nil
}
func (ds DataStore) CreateTables() error {
// Create users table
_, err := ds.db.Exec(`CREATE TABLE IF NOT EXISTS users (
UID TEXT PRIMARY KEY,
password BLOB
)`)
if err != nil {
return err
}
return nil
}
func (ds DataStore) GetPassword(uid string) ([]byte, error) {
var password []byte
err := ds.db.QueryRow("SELECT password FROM users WHERE UID = ?", uid).Scan(&password)
if err != nil {
return nil, err
}
return password, nil
}
func (ds DataStore) InsertUser(uid string, password []byte) error {
_, err := ds.db.Exec("INSERT INTO users (UID, password) VALUES (?, ?)", uid, password)
if err != nil {
return err
}
return nil
}

View file

@ -2,46 +2,140 @@ package gateway
import (
"PD1/internal/protocol"
"PD1/internal/utils/cryptoUtils"
"crypto/x509"
"fmt"
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func HandleGetMessage(c *gin.Context){
fmt.Println("Get Message Handler")
func HandleGetMessage(c *gin.Context) {
fmt.Println("Get Message Handler")
}
func HandleGetUnreadMsgsInfo(c *gin.Context){
fmt.Println("Get Unread Messages Info Handler")
func HandleGetUnreadMsgsInfo(c *gin.Context) {
fmt.Println("Get Unread Messages Info Handler")
}
func HandleGetUserCert(c *gin.Context){
fmt.Println("Get User Cert Handler")
func HandleGetUserCert(c *gin.Context) {
fmt.Println("Get User Cert Handler")
}
func HandleSendMessage(c *gin.Context){
fmt.Println("Send Message Handler")
func HandleSendMessage(c *gin.Context) {
fmt.Println("Send Message Handler")
}
func HandleRegister(c *gin.Context){
var postRegister protocol.PostRegister
c.Bind(postRegister)
//Hash the password (using salt) and store it on the db
//Get the username from the certificate
func HandleRegister(c *gin.Context, dataStore DataStore, keyStore cryptoUtils.KeyStore) {
var postRegister protocol.PostRegister
err := c.Bind(postRegister)
if err != nil {
c.JSON(http.StatusBadRequest,gin.H{"error": "Request body is not a PostRegister"})
return
}
//Check if the certificate pseudonym matches the uid in postRegister
//And if it's signed by the CA
userCert, err := x509.ParseCertificate(postRegister.Certificate)
if err != nil {
c.JSON(http.StatusBadRequest,gin.H{"error": "User certificate is invalid"})
return
}
oidMap := cryptoUtils.ExtractAllOIDValues(userCert)
//Check if certificate usage is MSG SERVICE
usage := oidMap["2.5.4.11"]
if usage != "MSG SERVICE" {
c.JSON(http.StatusBadRequest,gin.H{"error": "Certificate usage is not \"MSG SERVICE\""})
return
}
err = keyStore.CheckCert(userCert, postRegister.UID)
if err != nil {
c.JSON(http.StatusBadRequest,gin.H{"error": "User certificate is invalid, not trusted or belongs to another user"})
return
}
hashedPassword, err := HashPassword(postRegister.Password)
if err != nil {
log.Fatalln("Could not hash the password")
}
err = dataStore.InsertUser(postRegister.UID, hashedPassword)
if err != nil {
log.Fatalln("Could not insert user into DB")
}
//TODO: Send the certificate to the server
c.JSON(http.StatusOK, gin.H{})
}
func AuthMiddleware(c *gin.Context){
fmt.Println("Authentication Middleware")
func HandleLogin(c *gin.Context, dataStore DataStore, keyStore cryptoUtils.KeyStore) {
var postLogin protocol.PostLogin
err := c.Bind(postLogin)
if err != nil {
c.AbortWithStatus(http.StatusBadRequest)
}
hashedPassword, err := dataStore.GetPassword(postLogin.UID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user id or password"})
return
}
err = CheckPassword(hashedPassword, postLogin.Password)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user id or password"})
return
}
jwToken, err := GenerateJWT(postLogin.UID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to create token"})
}
//Send token to user
c.JSON(http.StatusOK, gin.H{"token":jwToken})
}
func Run(){
router := gin.Default()
auth := router.Group("/", func(c *gin.Context) {
AuthMiddleware(c)
})
func AuthMiddleware(c *gin.Context) {
fmt.Println("Authentication Middleware")
tokenList := c.Request.Header["Token"]
if tokenList == nil {
c.JSON(http.StatusUnauthorized,gin.H{"error": "No authentication token provided"})
}
// We only care about the first entry
token := tokenList[0]
uid, err := ValidateJWT(token)
if err!= nil {
c.JSON(http.StatusUnauthorized,gin.H{"error": "Token is invalid or has expired"})
}
c.Set("uid", uid)
c.Next()
}
func Run() {
dataStore, err := OpenDB()
if err != nil {
log.Fatalln("Error opening the database")
}
defer dataStore.db.Close()
passphrase := readStdin("Insert keystore passphrase")
keyStore, err := cryptoUtils.LoadKeyStore("certs/gateway/gateway.p12", passphrase)
if err != nil {
log.Fatalln(err.Error())
}
router := gin.Default()
auth := router.Group("/", func(c *gin.Context) {
AuthMiddleware(c)
})
auth.GET("/message/:num", func(c *gin.Context) {
HandleGetMessage(c)
@ -59,19 +153,22 @@ func Run(){
HandleSendMessage(c)
})
router.POST("/register",func(c *gin.Context) {
HandleRegister(c)
})
router.POST("/register", func(c *gin.Context) {
HandleRegister(c, dataStore, keyStore)
})
router.POST("/login", func(c *gin.Context) {
HandleLogin(c, dataStore, keyStore)
})
server := http.Server{
Addr: "0.0.0.0:8080",
Handler: router,
//TODO: Verify if it's the gateway
TLSConfig: serverKeyStore.GetServerTLSConfig(),
}
err = server.ListenAndServeTLS("", "")
if err!=nil {
log.Fatal(err.Error())
}
err := server.ListenAndServeTLS("", "")
if err != nil {
log.Fatal(err.Error())
}
}

View file

@ -0,0 +1,14 @@
package gateway
import (
"bufio"
"fmt"
"os"
)
func readStdin(message string) string {
fmt.Println(message)
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
return scanner.Text()
}

View file

@ -0,0 +1,62 @@
package gateway
import (
"errors"
"os"
"time"
"github.com/golang-jwt/jwt/v4"
"golang.org/x/crypto/bcrypt"
)
// HashPassword hashes the given password and returns the hashed password as a byte slice.
func HashPassword(password string) ([]byte, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
return hashedPassword, nil
}
// CheckPassword compares a bcrypt hashed password with its possible plaintext equivalent.
func CheckPassword(hashedPassword []byte, password string) error {
return bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
}
// GenerateJWT generates a JWT token with a specified user ID and expiry time.
func GenerateJWT(uid string) (string, error) {
claims := &jwt.MapClaims{
"sub": uid,
"exp": time.Now().Add(time.Hour * 24).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(os.Getenv("SECRET_KEY")))
if err != nil {
return "", err
}
return tokenString, nil
}
// ValidateJWT validates the given JWT token and returns the user id if valid.
func ValidateJWT(tokenString string) (string, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("SECRET_KEY")), nil
})
if err != nil {
return "", err
}
if !token.Valid {
return "", errors.New("invalid token")
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
if time.Now().Unix() > claims["exp"].(int64) {
return "", errors.New("JWT token has expired")
}
return claims["sub"].(string),nil
} else {
return "",errors.New("Failed to get jwt claims")
}
}