♻️ Improve code structure and much easier to read

🐛 Fix auth middleware
This commit is contained in:
2024-06-22 13:04:21 +08:00
parent c37a55b88b
commit 7007cda8f2
34 changed files with 451 additions and 337 deletions

View File

@ -2,16 +2,16 @@ package admin
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
func grantBadge(c *fiber.Ctx) error {
if err := utils.CheckPermissions(c, "AdminGrantBadges", true); err != nil {
if err := exts.EnsureGrantedPerm(c, "AdminGrantBadges", true); err != nil {
return err
}
@ -21,7 +21,7 @@ func grantBadge(c *fiber.Ctx) error {
AccountID uint `json:"account_id"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -44,7 +44,7 @@ func grantBadge(c *fiber.Ctx) error {
}
func revokeBadge(c *fiber.Ctx) error {
if err := utils.CheckPermissions(c, "AdminRevokeBadges", true); err != nil {
if err := exts.EnsureGrantedPerm(c, "AdminRevokeBadges", true); err != nil {
return err
}

View File

@ -4,8 +4,8 @@ import (
"github.com/gofiber/fiber/v2"
)
func MapAdminEndpoints(A *fiber.App, authMiddleware fiber.Handler) {
admin := A.Group("/api/admin").Use(authMiddleware)
func MapAdminEndpoints(A *fiber.App) {
admin := A.Group("/api/admin")
{
admin.Post("/badges", grantBadge)
admin.Delete("/badges/:badgeId", revokeBadge)

View File

@ -1,12 +1,11 @@
package server
package api
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"strconv"
"time"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
@ -16,7 +15,10 @@ import (
)
func getUserinfo(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data models.Account
if err := database.C.
@ -47,7 +49,10 @@ func getUserinfo(c *fiber.Ctx) error {
}
func getEvents(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
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)
@ -76,7 +81,10 @@ func getEvents(c *fiber.Ctx) error {
}
func editUserinfo(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
Nick string `json:"nick" validate:"required,min=4,max=24"`
@ -86,7 +94,7 @@ func editUserinfo(c *fiber.Ctx) error {
Birthday time.Time `json:"birthday"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -116,7 +124,10 @@ func editUserinfo(c *fiber.Ctx) error {
}
func killSession(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id, _ := c.ParamsInt("ticketId", 0)
if err := database.C.Delete(&models.AuthTicket{}, &models.AuthTicket{
@ -138,7 +149,7 @@ func doRegister(c *fiber.Ctx) error {
MagicToken string `json:"magic_token"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
} else if viper.GetBool("use_registration_magic_token") && len(data.MagicToken) <= 0 {
return fmt.Errorf("missing magic token in request")
@ -167,7 +178,7 @@ func doRegisterConfirm(c *fiber.Ctx) error {
Code string `json:"code" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,8 +1,8 @@
package server
package api
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"time"
"github.com/gofiber/fiber/v2"
@ -16,7 +16,7 @@ func doAuthenticate(c *fiber.Ctx) error {
Password string `json:"password" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -48,7 +48,7 @@ func doMultiFactorAuthenticate(c *fiber.Ctx) error {
Code string `json:"code" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -85,7 +85,7 @@ func getToken(c *fiber.Ctx) error {
GrantType string `json:"grant_type" form:"grant_type"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -134,7 +134,7 @@ func getToken(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "unsupported exchange token type")
}
services.SetJwtCookieSet(c, access, refresh)
exts.SetAuthCookies(c, access, refresh)
return c.JSON(fiber.Map{
"id_token": access,

View File

@ -1,4 +1,4 @@
package server
package api
import (
"context"
@ -7,20 +7,27 @@ import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/gap"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
)
func setAvatar(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
var data struct {
AttachmentID uint `json:"attachment" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -47,13 +54,16 @@ func setAvatar(c *fiber.Ctx) error {
}
func setBanner(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
AttachmentID uint `json:"attachment" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,4 +1,4 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"

View File

@ -1,14 +1,17 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
func listFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
status := c.QueryInt("status", -1)
var err error
@ -27,7 +30,10 @@ func listFriendship(c *fiber.Ctx) error {
}
func getFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
relatedId, _ := c.ParamsInt("relatedId", 0)
related, err := services.GetAccount(uint(relatedId))
@ -43,7 +49,10 @@ func getFriendship(c *fiber.Ctx) error {
}
func makeFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
relatedName := c.Query("related")
relatedId, _ := c.ParamsInt("relatedId", 0)
@ -72,14 +81,17 @@ func makeFriendship(c *fiber.Ctx) error {
}
func editFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
relatedId, _ := c.ParamsInt("relatedId", 0)
var data struct {
Status uint8 `json:"status"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -103,7 +115,10 @@ func editFriendship(c *fiber.Ctx) error {
}
func deleteFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
relatedId, _ := c.ParamsInt("relatedId", 0)
related, err := services.GetAccount(uint(relatedId))

View File

@ -0,0 +1,90 @@
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"github.com/gofiber/contrib/websocket"
"github.com/gofiber/fiber/v2"
)
func MapAPIs(app *fiber.App) {
app.Get("/.well-known", getMetadata)
app.Get("/.well-known/openid-configuration", getOidcConfiguration)
api := app.Group("/api").Name("API")
{
notify := api.Group("/notifications").Name("Notifications API")
{
notify.Get("/", getNotifications)
notify.Post("/subscribe", addNotifySubscriber)
notify.Put("/batch/read", markNotificationReadBatch)
notify.Put("/:notificationId/read", markNotificationRead)
}
me := api.Group("/users/me").Name("Myself Operations")
{
me.Put("/avatar", setAvatar)
me.Put("/banner", setBanner)
me.Get("/", getUserinfo)
me.Get("/page", getOwnPersonalPage)
me.Put("/", editUserinfo)
me.Put("/page", editPersonalPage)
me.Get("/events", getEvents)
me.Get("/tickets", getTickets)
me.Delete("/tickets/:ticketId", killSession)
me.Post("/confirm", doRegisterConfirm)
friends := me.Group("/friends").Name("Friends")
{
friends.Get("/", listFriendship)
friends.Get("/:relatedId", getFriendship)
friends.Post("/", makeFriendship)
friends.Post("/:relatedId", makeFriendship)
friends.Put("/:relatedId", editFriendship)
friends.Delete("/:relatedId", deleteFriendship)
}
}
directory := api.Group("/users/:alias").Name("User Directory")
{
directory.Get("/", getOtherUserinfo)
directory.Get("/page", getPersonalPage)
}
api.Post("/users", doRegister)
api.Post("/auth", doAuthenticate)
api.Post("/auth/token", getToken)
api.Post("/auth/factors/:factorId", requestFactorToken)
realms := api.Group("/realms").Name("Realms API")
{
realms.Get("/", listCommunityRealm)
realms.Get("/me", listOwnedRealm)
realms.Get("/me/available", listAvailableRealm)
realms.Get("/:realm", getRealm)
realms.Get("/:realm/members", listRealmMembers)
realms.Get("/:realm/members/me", getMyRealmMember)
realms.Post("/", createRealm)
realms.Put("/:realmId", editRealm)
realms.Delete("/:realmId", deleteRealm)
realms.Post("/:realm/members", addRealmMember)
realms.Delete("/:realm/members", removeRealmMember)
realms.Delete("/:realm/members/me", leaveRealm)
}
developers := api.Group("/dev").Name("Developers API")
{
developers.Post("/notify", notifyUser)
}
api.Use(func(c *fiber.Ctx) error {
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
return c.Next()
}).Get("/ws", websocket.New(listenWebsocket))
}
}

View File

@ -1,18 +1,22 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
func getNotifications(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
tx := database.C.Where(&models.Notification{RecipientID: user.ID}).Model(&models.Notification{})
var count int64
@ -36,9 +40,16 @@ func getNotifications(c *fiber.Ctx) error {
}
func markNotificationRead(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id, _ := c.ParamsInt("notificationId", 0)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
var notify models.Notification
if err := database.C.Where(&models.Notification{
BaseModel: models.BaseModel{ID: uint(id)},
@ -55,13 +66,16 @@ func markNotificationRead(c *fiber.Ctx) error {
}
func markNotificationReadBatch(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
MessageIDs []uint `json:"messages"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -75,7 +89,10 @@ func markNotificationReadBatch(c *fiber.Ctx) error {
}
func addNotifySubscriber(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
Provider string `json:"provider" validate:"required"`
@ -83,7 +100,7 @@ func addNotifySubscriber(c *fiber.Ctx) error {
DeviceID string `json:"device_id" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,9 +1,9 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
@ -21,7 +21,7 @@ func notifyUser(c *fiber.Ctx) error {
UserID uint `json:"user_id" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,9 +1,9 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"github.com/gofiber/fiber/v2"
)
@ -28,7 +28,10 @@ func getPersonalPage(c *fiber.Ctx) error {
}
func getOwnPersonalPage(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var page models.AccountPage
if err := database.C.
@ -41,14 +44,17 @@ func getOwnPersonalPage(c *fiber.Ctx) error {
}
func editPersonalPage(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
Content string `json:"content"`
Links []models.AccountPageLinks `json:"links"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,10 +1,10 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
@ -22,7 +22,10 @@ func listRealmMembers(c *fiber.Ctx) error {
func getMyRealmMember(c *fiber.Ctx) error {
alias := c.Params("realm")
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
if realm, err := services.GetRealmWithAlias(alias); err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
@ -34,14 +37,17 @@ func getMyRealmMember(c *fiber.Ctx) error {
}
func addRealmMember(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
alias := c.Params("realm")
var data struct {
Target string `json:"target" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -65,14 +71,17 @@ func addRealmMember(c *fiber.Ctx) error {
}
func removeRealmMember(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
alias := c.Params("realm")
var data struct {
Target string `json:"target" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -96,7 +105,10 @@ func removeRealmMember(c *fiber.Ctx) error {
}
func leaveRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
alias := c.Params("realm")
realm, err := services.GetRealmWithAlias(alias)

View File

@ -1,10 +1,10 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
@ -27,7 +27,10 @@ func listCommunityRealm(c *fiber.Ctx) error {
}
func listOwnedRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
if realms, err := services.ListOwnedRealm(user); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
} else {
@ -36,7 +39,10 @@ func listOwnedRealm(c *fiber.Ctx) error {
}
func listAvailableRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
if realms, err := services.ListAvailableRealm(user); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
} else {
@ -45,10 +51,10 @@ func listAvailableRealm(c *fiber.Ctx) error {
}
func createRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := utils.CheckPermissions(c, "CreateRealms", true); err != nil {
if err := exts.EnsureGrantedPerm(c, "CreateRealms", true); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
Alias string `json:"alias" validate:"required,lowercase,min=4,max=32"`
@ -58,7 +64,7 @@ func createRealm(c *fiber.Ctx) error {
IsCommunity bool `json:"is_community"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -78,7 +84,10 @@ func createRealm(c *fiber.Ctx) error {
}
func editRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id, _ := c.ParamsInt("realmId", 0)
var data struct {
@ -89,7 +98,7 @@ func editRealm(c *fiber.Ctx) error {
IsCommunity bool `json:"is_community"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -116,7 +125,10 @@ func editRealm(c *fiber.Ctx) error {
}
func deleteRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id, _ := c.ParamsInt("realmId", 0)
var realm models.Realm

View File

@ -1,13 +1,17 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"github.com/gofiber/fiber/v2"
)
func getTickets(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
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)

View File

@ -1,4 +1,4 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"

View File

@ -1,4 +1,4 @@
package server
package api
import (
"fmt"

View File

@ -1,8 +1,7 @@
package server
package api
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/contrib/websocket"
@ -12,7 +11,7 @@ import (
)
func listenWebsocket(c *websocket.Conn) {
user := c.Locals("principal").(models.Account)
user := c.Locals("user").(models.Account)
// Push connection
services.ClientRegister(user, c)

View File

@ -1,55 +0,0 @@
package server
import (
"strings"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2"
)
func authMiddleware(c *fiber.Ctx) error {
var token string
if cookie := c.Cookies(services.CookieAccessKey); len(cookie) > 0 {
token = cookie
}
if header := c.Get(fiber.HeaderAuthorization); len(header) > 0 {
tk := strings.Replace(header, "Bearer", "", 1)
token = strings.TrimSpace(tk)
}
if query := c.Query("tk"); len(query) > 0 {
token = strings.TrimSpace(query)
}
c.Locals("token", token)
if err := authFunc(c); err != nil {
return err
}
return c.Next()
}
func authFunc(c *fiber.Ctx, overrides ...string) error {
var token string
if len(overrides) > 0 {
token = overrides[0]
} else {
if tk, ok := c.Locals("token").(string); !ok {
return fiber.NewError(fiber.StatusUnauthorized)
} else {
token = tk
}
}
rtk := c.Cookies(services.CookieRefreshKey)
if ctx, perms, atk, rtk, err := services.Authenticate(token, rtk, 0); err == nil {
if atk != token {
services.SetJwtCookieSet(c, atk, rtk)
}
c.Locals("permissions", perms)
c.Locals("principal", ctx.Account)
return nil
} else {
return err
}
}

View File

@ -0,0 +1,53 @@
package exts
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/hyper"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2"
"strings"
)
func AuthMiddleware(c *fiber.Ctx) error {
var atk string
if cookie := c.Cookies(hyper.CookieAtk); len(cookie) > 0 {
atk = cookie
}
if header := c.Get(fiber.HeaderAuthorization); len(header) > 0 {
tk := strings.Replace(header, "Bearer", "", 1)
atk = strings.TrimSpace(tk)
}
c.Locals("p_token", atk)
rtk := c.Cookies(hyper.CookieRtk)
if ctx, perms, newAtk, newRtk, err := services.Authenticate(atk, rtk, 0); err == nil {
if newAtk != atk {
SetAuthCookies(c, newAtk, newRtk)
}
c.Locals("permissions", perms)
c.Locals("user", ctx.Account)
}
return c.Next()
}
func EnsureAuthenticated(c *fiber.Ctx) error {
if _, ok := c.Locals("user").(models.Account); !ok {
return fiber.NewError(fiber.StatusUnauthorized)
}
return nil
}
func EnsureGrantedPerm(c *fiber.Ctx, key string, val any) error {
if err := EnsureAuthenticated(c); err != nil {
return err
}
perms := c.Locals("permissions").(map[string]any)
if !services.HasPermNode(perms, key, val) {
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("missing permission: %s", key))
}
return nil
}

View File

@ -0,0 +1,27 @@
package exts
import (
"git.solsynth.dev/hydrogen/passport/pkg/hyper"
"github.com/gofiber/fiber/v2"
"github.com/spf13/viper"
"time"
)
func SetAuthCookies(c *fiber.Ctx, atk, rtk string) {
c.Cookie(&fiber.Cookie{
Name: hyper.CookieAtk,
Value: atk,
Domain: viper.GetString("security.cookie_domain"),
SameSite: viper.GetString("security.cookie_samesite"),
Expires: time.Now().Add(60 * time.Minute),
Path: "/",
})
c.Cookie(&fiber.Cookie{
Name: hyper.CookieRtk,
Value: rtk,
Domain: viper.GetString("security.cookie_domain"),
SameSite: viper.GetString("security.cookie_samesite"),
Expires: time.Now().Add(24 * 30 * time.Hour),
Path: "/",
})
}

View File

@ -0,0 +1,32 @@
package exts
import (
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
"github.com/sujit-baniya/flash"
)
var validation = validator.New(validator.WithRequiredStructEnabled())
func BindAndValidate(c *fiber.Ctx, out any) error {
if err := c.BodyParser(out); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
} else if err := validation.Struct(out); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return nil
}
func GetRedirectUri(c *fiber.Ctx, fallback ...string) *string {
if len(c.Query("redirect_uri")) > 0 {
return lo.ToPtr(c.Query("redirect_uri"))
} else if val, ok := flash.Get(c)["redirect_uri"].(*string); ok {
return val
} else if len(fallback) > 0 {
return &fallback[0]
} else {
return nil
}
}

View File

@ -1,11 +1,11 @@
package server
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/api"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"net/http"
"strings"
"github.com/gofiber/contrib/websocket"
"git.solsynth.dev/hydrogen/passport/pkg/internal"
"git.solsynth.dev/hydrogen/passport/pkg/internal/i18n"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/admin"
@ -61,92 +61,18 @@ func NewServer() {
Output: log.Logger,
}))
A.Use(exts.AuthMiddleware)
A.Use(i18n.I18nMiddleware)
A.Get("/.well-known", getMetadata)
A.Get("/.well-known/openid-configuration", getOidcConfiguration)
api := A.Group("/api").Name("API")
{
notify := api.Group("/notifications").Name("Notifications API")
{
notify.Get("/", authMiddleware, getNotifications)
notify.Post("/subscribe", authMiddleware, addNotifySubscriber)
notify.Put("/batch/read", authMiddleware, markNotificationReadBatch)
notify.Put("/:notificationId/read", authMiddleware, markNotificationRead)
}
me := api.Group("/users/me").Name("Myself Operations")
{
me.Put("/avatar", authMiddleware, setAvatar)
me.Put("/banner", authMiddleware, setBanner)
me.Get("/", authMiddleware, getUserinfo)
me.Get("/page", authMiddleware, getOwnPersonalPage)
me.Put("/", authMiddleware, editUserinfo)
me.Put("/page", authMiddleware, editPersonalPage)
me.Get("/events", authMiddleware, getEvents)
me.Get("/tickets", authMiddleware, getTickets)
me.Delete("/tickets/:ticketId", authMiddleware, killSession)
me.Post("/confirm", doRegisterConfirm)
friends := me.Group("/friends").Name("Friends")
{
friends.Get("/", authMiddleware, listFriendship)
friends.Get("/:relatedId", authMiddleware, getFriendship)
friends.Post("/", authMiddleware, makeFriendship)
friends.Post("/:relatedId", authMiddleware, makeFriendship)
friends.Put("/:relatedId", authMiddleware, editFriendship)
friends.Delete("/:relatedId", authMiddleware, deleteFriendship)
}
}
directory := api.Group("/users/:alias").Name("User Directory")
{
directory.Get("/", getOtherUserinfo)
directory.Get("/page", getPersonalPage)
}
api.Post("/users", doRegister)
api.Post("/auth", doAuthenticate)
api.Post("/auth/token", getToken)
api.Post("/auth/factors/:factorId", requestFactorToken)
realms := api.Group("/realms").Name("Realms API")
{
realms.Get("/", listCommunityRealm)
realms.Get("/me", authMiddleware, listOwnedRealm)
realms.Get("/me/available", authMiddleware, listAvailableRealm)
realms.Get("/:realm", getRealm)
realms.Get("/:realm/members", listRealmMembers)
realms.Get("/:realm/members/me", authMiddleware, getMyRealmMember)
realms.Post("/", authMiddleware, createRealm)
realms.Put("/:realmId", authMiddleware, editRealm)
realms.Delete("/:realmId", authMiddleware, deleteRealm)
realms.Post("/:realm/members", authMiddleware, addRealmMember)
realms.Delete("/:realm/members", authMiddleware, removeRealmMember)
realms.Delete("/:realm/members/me", authMiddleware, leaveRealm)
}
developers := api.Group("/dev").Name("Developers API")
{
developers.Post("/notify", notifyUser)
}
api.Get("/ws", authMiddleware, websocket.New(listenWebsocket))
}
A.Use(favicon.New(favicon.Config{
FileSystem: http.FS(pkg.FS),
File: "views/favicon.png",
URL: "/favicon.png",
}))
admin.MapAdminEndpoints(A, authMiddleware)
ui.MapUserInterface(A, authFunc)
api.MapAPIs(A)
admin.MapAdminEndpoints(A)
ui.MapUserInterface(A)
}
func Listen() {

View File

@ -2,6 +2,7 @@ package ui
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"html/template"
"time"
@ -15,7 +16,10 @@ import (
)
func selfUserinfoPage(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return DoAuthRedirect(c)
}
user := c.Locals("user").(models.Account)
var data models.Account
if err := database.C.

View File

@ -3,28 +3,15 @@ package ui
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
func MapUserInterface(A *fiber.App, authFunc utils.AuthFunc) {
authCheckWare := func(c *fiber.Ctx) error {
var token string
if cookie := c.Cookies(services.CookieAccessKey); len(cookie) > 0 {
token = cookie
}
c.Locals("token", token)
if err := authFunc(c); err != nil {
uri := c.Request().URI().FullURI()
return c.Redirect(fmt.Sprintf("/sign-in?redirect_uri=%s", string(uri)))
} else {
return c.Next()
}
}
func DoAuthRedirect(c *fiber.Ctx) error {
uri := c.Request().URI().FullURI()
return c.Redirect(fmt.Sprintf("/sign-in?redirect_uri=%s", string(uri)))
}
func MapUserInterface(A *fiber.App) {
pages := A.Group("/").Name("Pages")
pages.Get("/", func(c *fiber.Ctx) error {
@ -35,13 +22,13 @@ func MapUserInterface(A *fiber.App, authFunc utils.AuthFunc) {
pages.Get("/sign-in", signinPage)
pages.Get("/mfa", mfaRequestPage)
pages.Get("/mfa/apply", mfaApplyPage)
pages.Get("/authorize", authCheckWare, authorizePage)
pages.Get("/authorize", authorizePage)
pages.Post("/sign-up", signupAction)
pages.Post("/sign-in", signinAction)
pages.Post("/mfa", mfaRequestAction)
pages.Post("/mfa/apply", mfaApplyAction)
pages.Post("/authorize", authCheckWare, authorizeAction)
pages.Post("/authorize", authorizeAction)
pages.Get("/users/me", authCheckWare, selfUserinfoPage)
pages.Get("/users/me", selfUserinfoPage)
}

View File

@ -3,8 +3,8 @@ package ui
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/samber/lo"
@ -68,7 +68,7 @@ func mfaRequestAction(c *fiber.Ctx) error {
}
redirectBackUri := "/sign-in"
err := utils.BindAndValidate(c, &data)
err := exts.BindAndValidate(c, &data)
if data.TicketID > 0 {
redirectBackUri = fmt.Sprintf("/mfa?ticket=%d", data.TicketID)
@ -95,7 +95,7 @@ func mfaRequestAction(c *fiber.Ctx) error {
}
return flash.WithData(c, fiber.Map{
"redirect_uri": utils.GetRedirectUri(c),
"redirect_uri": exts.GetRedirectUri(c),
}).Redirect(fmt.Sprintf("/mfa/apply?ticket=%d&factor=%d", data.TicketID, factor.ID))
}
@ -145,7 +145,7 @@ func mfaApplyAction(c *fiber.Ctx) error {
}
redirectBackUri := "/sign-in"
err := utils.BindAndValidate(c, &data)
err := exts.BindAndValidate(c, &data)
if data.TicketID > 0 {
redirectBackUri = fmt.Sprintf("/mfa/apply?ticket=%d&factor=%d", data.TicketID, data.FactorID)
@ -187,8 +187,8 @@ func mfaApplyAction(c *fiber.Ctx) error {
"message": fmt.Sprintf("failed to exchange token: %v", err.Error()),
}).Redirect("/sign-in")
} else {
services.SetJwtCookieSet(c, access, refresh)
exts.SetAuthCookies(c, access, refresh)
}
return c.Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/users/me")))
return c.Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/users/me")))
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
@ -16,7 +17,11 @@ import (
func authorizePage(c *fiber.Ctx) error {
localizer := c.Locals("localizer").(*i18n.Localizer)
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return DoAuthRedirect(c)
}
user := c.Locals("user").(models.Account)
id := c.Query("client_id")
redirect := c.Query("redirect_uri")
@ -81,12 +86,19 @@ func authorizePage(c *fiber.Ctx) error {
}
func authorizeAction(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id := c.Query("client_id")
response := c.Query("response_type")
redirect := c.Query("redirect_uri")
scope := c.Query("scope")
if err := exts.EnsureAuthenticated(c); err != nil {
return DoAuthRedirect(c)
}
redirectBackUri := "/authorize?" + string(c.Request().URI().QueryString())
if len(scope) <= 0 {

View File

@ -2,8 +2,8 @@ package ui
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/samber/lo"
@ -47,7 +47,7 @@ func signinAction(c *fiber.Ctx) error {
Password string `form:"password" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return flash.WithInfo(c, fiber.Map{
"message": err.Error(),
}).Redirect("/sign-in")
@ -76,7 +76,7 @@ func signinAction(c *fiber.Ctx) error {
if ticket.IsAvailable() != nil {
return flash.WithData(c, fiber.Map{
"redirect_uri": utils.GetRedirectUri(c),
"redirect_uri": exts.GetRedirectUri(c),
}).Redirect(fmt.Sprintf("/mfa?ticket=%d", ticket.ID))
}
@ -86,8 +86,8 @@ func signinAction(c *fiber.Ctx) error {
"message": fmt.Sprintf("failed to exchange token: %v", err.Error()),
}).Redirect("/sign-in")
} else {
services.SetJwtCookieSet(c, access, refresh)
exts.SetAuthCookies(c, access, refresh)
}
return c.Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/users/me")))
return c.Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/users/me")))
}

View File

@ -4,8 +4,8 @@ import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/samber/lo"
@ -52,7 +52,7 @@ func signupAction(c *fiber.Ctx) error {
MagicToken string `form:"magic_token"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return flash.WithInfo(c, fiber.Map{
"message": err.Error(),
}).Redirect("/sign-up")
@ -82,6 +82,6 @@ func signupAction(c *fiber.Ctx) error {
} else {
return flash.WithInfo(c, fiber.Map{
"message": "account has been created. now you can sign in!",
}).Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/sign-in")))
}).Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/sign-in")))
}
}