diff --git a/controllers/access.go b/controllers/access.go new file mode 100644 index 0000000..aa2159a --- /dev/null +++ b/controllers/access.go @@ -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) +} diff --git a/controllers/auth.go b/controllers/auth.go new file mode 100644 index 0000000..afe16a0 --- /dev/null +++ b/controllers/auth.go @@ -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) +} diff --git a/controllers/session.go b/controllers/session.go new file mode 100644 index 0000000..0aab33a --- /dev/null +++ b/controllers/session.go @@ -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) +} diff --git a/controllers/user.go b/controllers/user.go new file mode 100644 index 0000000..22990f3 --- /dev/null +++ b/controllers/user.go @@ -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) +}