List, create & delete auth factor apis

This commit is contained in:
LittleSheep 2025-01-27 19:19:31 +08:00
parent a43e89a5a3
commit dc2de65245
6 changed files with 172 additions and 59 deletions

View File

@ -16,6 +16,8 @@ type AuthFactorType = int8
const ( const (
PasswordAuthFactor = AuthFactorType(iota) PasswordAuthFactor = AuthFactorType(iota)
EmailPasswordFactor EmailPasswordFactor
InAppNotifyFactor
TimeOtpFactor
) )
type AuthFactor struct { type AuthFactor struct {

View File

@ -83,39 +83,7 @@ func getUserinfo(c *fiber.Ctx) error {
return c.JSON(resp) return c.JSON(resp)
} }
func getEvents(c *fiber.Ctx) error { func updateUserinfo(c *fiber.Ctx) error {
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)
var count int64
var events []models.ActionEvent
if err := database.C.
Where(&models.ActionEvent{AccountID: user.ID}).
Model(&models.ActionEvent{}).
Count(&count).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
if err := database.C.
Order("created_at desc").
Where(&models.ActionEvent{AccountID: user.ID}).
Limit(take).
Offset(offset).
Find(&events).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return c.JSON(fiber.Map{
"count": count,
"data": events,
})
}
func editUserinfo(c *fiber.Ctx) error {
if err := exts.EnsureAuthenticated(c); err != nil { if err := exts.EnsureAuthenticated(c); err != nil {
return err return err
} }

View File

@ -0,0 +1,40 @@
package api
import (
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
"github.com/gofiber/fiber/v2"
)
func getEvents(c *fiber.Ctx) error {
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)
var count int64
var events []models.ActionEvent
if err := database.C.
Where(&models.ActionEvent{AccountID: user.ID}).
Model(&models.ActionEvent{}).
Count(&count).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
if err := database.C.
Order("created_at desc").
Where(&models.ActionEvent{AccountID: user.ID}).
Limit(take).
Offset(offset).
Find(&events).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return c.JSON(fiber.Map{
"count": count,
"data": events,
})
}

View File

@ -2,9 +2,13 @@ package api
import ( import (
"fmt" "fmt"
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts" "git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
"git.solsynth.dev/hypernet/passport/pkg/internal/services" "git.solsynth.dev/hypernet/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/samber/lo"
) )
func getAvailableFactors(c *fiber.Ctx) error { func getAvailableFactors(c *fiber.Ctx) error {
@ -42,42 +46,87 @@ func requestFactorToken(c *fiber.Ctx) error {
} }
} }
func requestResetPassword(c *fiber.Ctx) error { func listFactor(c *fiber.Ctx) error {
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var factors []models.AuthFactor
if err := database.C.Where("account_id = ?", user.ID).Find(&factors).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return c.JSON(factors)
}
func createFactor(c *fiber.Ctx) error {
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct { var data struct {
UserID uint `json:"user_id" validate:"required"` Type models.AuthFactorType `json:"type"`
Secret string `json:"secret"`
} }
if err := exts.BindAndValidate(c, &data); err != nil { if err := exts.BindAndValidate(c, &data); err != nil {
return err return err
} }
user, err := services.GetAccount(data.UserID) typeWhitelist := []models.AuthFactorType{
if err != nil { models.EmailPasswordFactor,
return fiber.NewError(fiber.StatusBadRequest, err.Error()) models.InAppNotifyFactor,
models.TimeOtpFactor,
}
if !lo.Contains(typeWhitelist, data.Type) {
return fiber.NewError(fiber.StatusBadRequest, "invalid factor type")
} }
if err = services.CheckAbleToResetPassword(user); err != nil { // Currently, each type of factor can only be created once
return fiber.NewError(fiber.StatusBadRequest, err.Error()) var currentCount int64
} else if err = services.RequestResetPassword(user); err != nil { if err := database.C.Model(&models.AuthFactor{}).
Where("account_id = ? AND type = ?", user.ID, data.Type).
Count(&currentCount).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("unable to check current factor count: %v", err))
} else if currentCount > 0 {
return fiber.NewError(fiber.StatusBadRequest, "this type of factor already exists")
}
factor := models.AuthFactor{
Type: data.Type,
Secret: data.Secret,
Account: user,
AccountID: user.ID,
}
if err := database.C.Create(&factor).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return c.JSON(factor)
}
func deleteFactor(c *fiber.Ctx) error {
id, _ := c.ParamsInt("factorId", 0)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var factor models.AuthFactor
if err := database.C.Where("id = ? AND account_id = ?", id, user.ID).First(&factor).Error; err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
if factor.Type == models.PasswordAuthFactor {
return fiber.NewError(fiber.StatusBadRequest, "unable to delete password factor")
}
if err := database.C.Delete(&factor).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
return c.SendStatus(fiber.StatusOK) return c.SendStatus(fiber.StatusOK)
} }
func confirmResetPassword(c *fiber.Ctx) error {
var data struct {
Code string `json:"code" validate:"required"`
NewPassword string `json:"new_password" validate:"required"`
}
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
if err := services.ConfirmResetPassword(data.Code, data.NewPassword); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.SendStatus(fiber.StatusOK)
}

View File

@ -60,7 +60,7 @@ func MapAPIs(app *fiber.App, baseURL string) {
me.Put("/banner", setBanner) me.Put("/banner", setBanner)
me.Get("/", getUserinfo) me.Get("/", getUserinfo)
me.Put("/", editUserinfo) me.Put("/", updateUserinfo)
me.Get("/events", getEvents) me.Get("/events", getEvents)
me.Get("/tickets", getTickets) me.Get("/tickets", getTickets)
me.Delete("/tickets/:ticketId", killTicket) me.Delete("/tickets/:ticketId", killTicket)
@ -72,6 +72,13 @@ func MapAPIs(app *fiber.App, baseURL string) {
me.Put("/status", editStatus) me.Put("/status", editStatus)
me.Delete("/status", clearStatus) me.Delete("/status", clearStatus)
factors := me.Group("/factors").Name("Factors")
{
factors.Get("/", listFactor)
factors.Post("/", createFactor)
factors.Delete("/:factorId", deleteFactor)
}
relations := me.Group("/relations").Name("Relations") relations := me.Group("/relations").Name("Relations")
{ {
relations.Post("/", makeFriendship) relations.Post("/", makeFriendship)

View File

@ -0,0 +1,47 @@
package api
import (
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2"
)
func requestResetPassword(c *fiber.Ctx) error {
var data struct {
UserID uint `json:"user_id" validate:"required"`
}
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
user, err := services.GetAccount(data.UserID)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
if err = services.CheckAbleToResetPassword(user); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
} else if err = services.RequestResetPassword(user); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return c.SendStatus(fiber.StatusOK)
}
func confirmResetPassword(c *fiber.Ctx) error {
var data struct {
Code string `json:"code" validate:"required"`
NewPassword string `json:"new_password" validate:"required"`
}
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
if err := services.ConfirmResetPassword(data.Code, data.NewPassword); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.SendStatus(fiber.StatusOK)
}