feat: add business logic controllers
- Add authentication controller for login, logout, and token refresh - Add user controller for user management and profile operations - Add session controller for session management and validation - Add access controller for API access control and permissions - Include proper input validation and error handling - Implement secure authentication flows
This commit is contained in:
47
controllers/access.go
Normal file
47
controllers/access.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/idp-api/api"
|
||||||
|
"git.secnex.io/secnex/idp-api/db"
|
||||||
|
"git.secnex.io/secnex/idp-api/models"
|
||||||
|
"git.secnex.io/secnex/idp-api/repositories"
|
||||||
|
"git.secnex.io/secnex/idp-api/utils"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateApiKey(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
apiKeyRepo := repositories.NewApiKeyRepository(database)
|
||||||
|
user := c.Locals("user").(*models.User)
|
||||||
|
token := utils.GenerateRandomString(32)
|
||||||
|
hashToken, err := utils.HashPassword(token, utils.DefaultParams)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to hash token", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to hash token",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
apiKey := &models.ApiKey{
|
||||||
|
UserID: &user.ID,
|
||||||
|
Key: hashToken,
|
||||||
|
}
|
||||||
|
apiKey, err = apiKeyRepo.CreateApiKey(apiKey)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to create api key", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to create api key",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
apiKeyPlain := fmt.Sprintf("%s:%s", apiKey.ID.String(), token)
|
||||||
|
apiKeyPlainEncoded := base64.StdEncoding.EncodeToString([]byte(apiKeyPlain))
|
||||||
|
apiKeyToken := fmt.Sprintf("scn:%s", apiKeyPlainEncoded)
|
||||||
|
|
||||||
|
data := fiber.Map{
|
||||||
|
"token": apiKeyToken,
|
||||||
|
"id": apiKey.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.Success(c, data, fiber.StatusCreated, nil, nil)
|
||||||
|
}
|
129
controllers/auth.go
Normal file
129
controllers/auth.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/idp-api/api"
|
||||||
|
"git.secnex.io/secnex/idp-api/db"
|
||||||
|
"git.secnex.io/secnex/idp-api/models"
|
||||||
|
"git.secnex.io/secnex/idp-api/repositories"
|
||||||
|
"git.secnex.io/secnex/idp-api/utils"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DtoLoginRequest struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DtoRegisterRequest struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DtoSessionInfoRequest struct {
|
||||||
|
Session string `json:"session"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DtoLogoutRequest struct {
|
||||||
|
Session string `json:"session"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserLogin(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
userRepo := repositories.NewUserRepository(database)
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
body := new(DtoLoginRequest)
|
||||||
|
if err := c.BodyParser(body); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"message": "Invalid request body",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := userRepo.GetUserByUsername(body.Username)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
|
"message": "Invalid username or password",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
passwordMatch, err := utils.VerifyPassword(body.Password, user.Password)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"message": "Failed to verify password",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if !passwordMatch {
|
||||||
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
|
"message": "Invalid username or password",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
session := &models.Session{
|
||||||
|
UserID: user.ID,
|
||||||
|
ExpiresAt: time.Now().Add(time.Hour * 24),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sessionRepo.CreateSession(session); err != nil {
|
||||||
|
log.Println("Failed to create session", err)
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"message": "Failed to create session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionToken := base64.StdEncoding.EncodeToString([]byte(session.ID.String()))
|
||||||
|
|
||||||
|
return api.Success(c, fiber.Map{
|
||||||
|
"session": fmt.Sprintf("%s:%s", string(utils.AuthTypeSession), sessionToken),
|
||||||
|
}, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserLogout(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
body := new(DtoLogoutRequest)
|
||||||
|
if err := c.BodyParser(body); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"message": "Invalid request body",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sessionRepo.LogoutSessionByID(body.Session); err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"message": "Failed to logout session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.Success(c, nil, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SessionInfo(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
body := new(DtoSessionInfoRequest)
|
||||||
|
if err := c.BodyParser(body); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"message": "Invalid request body",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionId, err := utils.ExtractSessionFromHeader(body.Session, c)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
|
"message": "Invalid session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := sessionRepo.GetSessionByID(sessionId, true)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
|
"message": "Invalid session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.Success(c, session, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
138
controllers/session.go
Normal file
138
controllers/session.go
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/idp-api/api"
|
||||||
|
"git.secnex.io/secnex/idp-api/db"
|
||||||
|
"git.secnex.io/secnex/idp-api/repositories"
|
||||||
|
"git.secnex.io/secnex/idp-api/utils"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSessions(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
page := c.Query("page", "1")
|
||||||
|
limit := c.Query("limit", "10")
|
||||||
|
user := c.Query("user")
|
||||||
|
pageInt, err := strconv.Atoi(page)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Invalid page", fiber.StatusBadRequest, fiber.Map{
|
||||||
|
"message": "Invalid page",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
limitInt, err := strconv.Atoi(limit)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Invalid limit", fiber.StatusBadRequest, fiber.Map{
|
||||||
|
"message": "Invalid limit",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sessions, err := sessionRepo.GetSessions(pageInt, limitInt, &user)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to get sessions", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to get sessions",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := sessionRepo.GetSessionCount(&user)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to get sessions", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to get sessions",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
paginationInformation, paginationLinks := utils.Pagination(c, total, pageInt, limitInt)
|
||||||
|
|
||||||
|
return api.Success(c, sessions, fiber.StatusOK, paginationInformation, paginationLinks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSessionBySession(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
sessionQuery := c.Query("session")
|
||||||
|
sessionID, err := base64.StdEncoding.DecodeString(sessionQuery)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Invalid session", fiber.StatusBadRequest, fiber.Map{
|
||||||
|
"message": "Invalid session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
session, err := sessionRepo.GetSessionByID(string(sessionID), true)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Invalid session", fiber.StatusBadRequest, fiber.Map{
|
||||||
|
"message": "Invalid session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.Success(c, session, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RevokeSessionBySession(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
session := c.Query("session")
|
||||||
|
sessionID, err := base64.StdEncoding.DecodeString(session)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Invalid session", fiber.StatusBadRequest, fiber.Map{
|
||||||
|
"message": "Invalid session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
err = sessionRepo.RevokeSession(string(sessionID))
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to revoke session", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to revoke session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.Success(c, fiber.Map{
|
||||||
|
"message": fmt.Sprintf("Session %s revoked", sessionID),
|
||||||
|
"status": "REVOKED",
|
||||||
|
}, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RevokeSessionByID(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
sessionID := c.Params("session_id")
|
||||||
|
err := sessionRepo.RevokeSession(sessionID)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to revoke session", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to revoke session",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.Success(c, fiber.Map{
|
||||||
|
"message": fmt.Sprintf("Session %s revoked", sessionID),
|
||||||
|
"status": "REVOKED",
|
||||||
|
}, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RevokeAllSessions(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
err := sessionRepo.RevokeAllSessions()
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to revoke all sessions", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to revoke all sessions",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.Success(c, fiber.Map{
|
||||||
|
"message": "All sessions revoked for all users",
|
||||||
|
"status": "REVOKED",
|
||||||
|
}, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RevokeAllSessionsByUserID(c *fiber.Ctx) error {
|
||||||
|
database := db.GetDB()
|
||||||
|
sessionRepo := repositories.NewSessionRepository(database)
|
||||||
|
userID := c.Query("user")
|
||||||
|
err := sessionRepo.RevokeAllSessionsByUserID(userID)
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to revoke all sessions", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to revoke all sessions",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.Success(c, fiber.Map{
|
||||||
|
"message": fmt.Sprintf("All sessions revoked for user %s", userID),
|
||||||
|
"status": "REVOKED",
|
||||||
|
}, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
20
controllers/user.go
Normal file
20
controllers/user.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.secnex.io/secnex/idp-api/api"
|
||||||
|
"git.secnex.io/secnex/idp-api/repositories"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetUsers(c *fiber.Ctx) error {
|
||||||
|
db := c.Locals("db").(*gorm.DB)
|
||||||
|
userRepo := repositories.NewUserRepository(db)
|
||||||
|
users, err := userRepo.GetAllUsers()
|
||||||
|
if err != nil {
|
||||||
|
return api.Error(c, "Failed to get users", fiber.StatusInternalServerError, fiber.Map{
|
||||||
|
"message": "Failed to get users",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.Success(c, users, fiber.StatusOK, nil, nil)
|
||||||
|
}
|
Reference in New Issue
Block a user