🗑️ Remove bus deps and code

This commit is contained in:
2024-01-27 01:11:32 +08:00
parent 69bae57473
commit d484b6b973
13 changed files with 273 additions and 369 deletions

View File

@ -1,13 +1,13 @@
package main
import (
"code.smartsheep.studio/hydrogen/passport/pkg/server"
"os"
"os/signal"
"syscall"
passport "code.smartsheep.studio/hydrogen/passport/pkg"
"code.smartsheep.studio/hydrogen/passport/pkg/database"
"code.smartsheep.studio/hydrogen/passport/pkg/server"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
@ -37,10 +37,9 @@ func main() {
log.Fatal().Err(err).Msg("An error occurred when running database auto migration.")
}
// Create connection between bus
server.InitConnection(viper.GetString("host"), viper.GetString("id"))
server.PublishCommands(server.C)
go server.C.ListenToServer()
// Server
server.NewServer()
go server.Listen()
// Messages
log.Info().Msgf("Passport v%s is started...", passport.AppVersion)

View File

@ -1,21 +1,23 @@
package server
import (
"code.smartsheep.studio/hydrogen/bus/pkg/kit/adaptor"
"code.smartsheep.studio/hydrogen/bus/pkg/kit/publisher"
"code.smartsheep.studio/hydrogen/bus/pkg/wire"
"code.smartsheep.studio/hydrogen/passport/pkg/database"
"code.smartsheep.studio/hydrogen/passport/pkg/models"
"code.smartsheep.studio/hydrogen/passport/pkg/security"
"github.com/gofiber/fiber/v2"
)
func doRegister(c *publisher.RequestCtx) error {
data := adaptor.ParseAnyToStruct[struct {
func doRegister(c *fiber.Ctx) error {
var data struct {
Name string `json:"name"`
Nick string `json:"nick"`
Email string `json:"email"`
Password string `json:"password"`
}](c.Parameters)
}
if err := BindAndValidate(c, &data); err != nil {
return err
}
user := models.Account{
Name: data.Name,
@ -37,8 +39,8 @@ func doRegister(c *publisher.RequestCtx) error {
}
if err := database.C.Create(&user).Error; err != nil {
return c.SendError(wire.InvalidActions, err)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.SendResponse(user)
return c.JSON(user)
}

View File

@ -1,32 +1,36 @@
package server
import (
"code.smartsheep.studio/hydrogen/bus/pkg/kit/adaptor"
"code.smartsheep.studio/hydrogen/bus/pkg/kit/publisher"
"code.smartsheep.studio/hydrogen/bus/pkg/wire"
"code.smartsheep.studio/hydrogen/passport/pkg/security"
"code.smartsheep.studio/hydrogen/passport/pkg/services"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/keyauth"
)
func doAuth(c *publisher.RequestCtx) error {
token := adaptor.ParseAnyToStruct[string](c.Parameters)
var auth = keyauth.New(keyauth.Config{
KeyLookup: "header:Authorization",
AuthScheme: "Bearer",
Validator: func(c *fiber.Ctx, token string) (bool, error) {
claims, err := security.DecodeJwt(token)
if err != nil {
return false, err
}
claims, err := security.DecodeJwt(token)
if err != nil {
return c.SendError(wire.Unauthorized, err)
}
session, err := services.LookupSessionWithToken(claims.ID)
if err != nil {
return false, err
} else if err := session.IsAvailable(); err != nil {
return false, err
}
session, err := services.LookupSessionWithToken(claims.ID)
if err != nil {
return c.SendError(wire.Unauthorized, err)
} else if err := session.IsAvailable(); err != nil {
return c.SendError(wire.Unauthorized, err)
}
user, err := services.GetAccount(session.AccountID)
if err != nil {
return false, err
}
user, err := services.GetAccount(session.AccountID)
if err != nil {
return c.SendError(wire.Unauthorized, err)
}
c.Locals("permissions", user.Permissions.Data())
return c.SendResponse(user.Permissions.Data())
}
return true, nil
},
ContextKey: "token",
})

View File

@ -1,116 +0,0 @@
package server
import (
"time"
"code.smartsheep.studio/hydrogen/bus/pkg/kit/adaptor"
"code.smartsheep.studio/hydrogen/bus/pkg/kit/publisher"
"code.smartsheep.studio/hydrogen/bus/pkg/wire"
"code.smartsheep.studio/hydrogen/passport/pkg/security"
"code.smartsheep.studio/hydrogen/passport/pkg/services"
"github.com/samber/lo"
)
func startChallenge(c *publisher.RequestCtx) error {
meta := adaptor.ParseAnyToStruct[wire.ClientMetadata](c.Metadata)
data := adaptor.ParseAnyToStruct[struct {
ID string `json:"id"`
}](c.Parameters)
user, err := services.LookupAccount(data.ID)
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
factors, err := services.LookupFactorsByUser(user.ID)
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
challenge, err := security.NewChallenge(user, factors, meta.ClientIp, meta.UserAgent)
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
return c.SendResponse(map[string]any{
"display_name": user.Nick,
"challenge": challenge,
"factors": factors,
})
}
func doChallenge(c *publisher.RequestCtx) error {
meta := adaptor.ParseAnyToStruct[wire.ClientMetadata](c.Metadata)
data := adaptor.ParseAnyToStruct[struct {
ChallengeID uint `json:"challenge_id"`
FactorID uint `json:"factor_id"`
Secret string `json:"secret"`
}](c.Parameters)
challenge, err := services.LookupChallengeWithFingerprint(data.ChallengeID, meta.ClientIp, meta.UserAgent)
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
factor, err := services.LookupFactor(data.FactorID)
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
if err := security.DoChallenge(challenge, factor, data.Secret); err != nil {
return c.SendError(wire.InvalidActions, err)
}
challenge, err = services.LookupChallenge(data.ChallengeID)
if err != nil {
return c.SendError(wire.InvalidActions, err)
} else if challenge.Progress >= challenge.Requirements {
session, err := security.GrantSession(challenge, []string{"*"}, nil, lo.ToPtr(time.Now()))
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
return c.SendResponse(map[string]any{
"is_finished": true,
"challenge": challenge,
"session": session,
})
}
return c.SendResponse(map[string]any{
"is_finished": false,
"challenge": challenge,
"session": nil,
})
}
func exchangeToken(c *publisher.RequestCtx) error {
data := adaptor.ParseAnyToStruct[struct {
GrantToken string `json:"token"`
}](c.Parameters)
access, refresh, err := security.ExchangeToken(data.GrantToken)
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
return c.SendResponse(map[string]any{
"access_token": access,
"refresh_token": refresh,
})
}
func refreshToken(c *publisher.RequestCtx) error {
data := adaptor.ParseAnyToStruct[struct {
RefreshToken string `json:"token"`
}](c.Parameters)
access, refresh, err := security.RefreshToken(data.RefreshToken)
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
return c.SendResponse(map[string]any{
"access_token": access,
"refresh_token": refresh,
})
}

View File

@ -0,0 +1,124 @@
package server
import (
"github.com/gofiber/fiber/v2"
"time"
"code.smartsheep.studio/hydrogen/passport/pkg/security"
"code.smartsheep.studio/hydrogen/passport/pkg/services"
"github.com/samber/lo"
)
func startChallenge(c *fiber.Ctx) error {
var data struct {
ID string `json:"id"`
}
if err := BindAndValidate(c, &data); err != nil {
return err
}
user, err := services.LookupAccount(data.ID)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
factors, err := services.LookupFactorsByUser(user.ID)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
challenge, err := security.NewChallenge(user, factors, c.IP(), c.Get(fiber.HeaderUserAgent))
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
return c.JSON(fiber.Map{
"display_name": user.Nick,
"challenge": challenge,
"factors": factors,
})
}
func doChallenge(c *fiber.Ctx) error {
var data struct {
ChallengeID uint `json:"challenge_id"`
FactorID uint `json:"factor_id"`
Secret string `json:"secret"`
}
if err := BindAndValidate(c, &data); err != nil {
return err
}
challenge, err := services.LookupChallengeWithFingerprint(data.ChallengeID, c.IP(), c.Get(fiber.HeaderUserAgent))
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
factor, err := services.LookupFactor(data.FactorID)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
if err := security.DoChallenge(challenge, factor, data.Secret); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
challenge, err = services.LookupChallenge(data.ChallengeID)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
} else if challenge.Progress >= challenge.Requirements {
session, err := security.GrantSession(challenge, []string{"*"}, nil, lo.ToPtr(time.Now()))
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.JSON(fiber.Map{
"is_finished": true,
"challenge": challenge,
"session": session,
})
}
return c.JSON(fiber.Map{
"is_finished": false,
"challenge": challenge,
"session": nil,
})
}
func exchangeToken(c *fiber.Ctx) error {
var data struct {
Token string `json:"token"`
GrantType string `json:"grant_type"`
}
if err := BindAndValidate(c, &data); err != nil {
return err
}
switch data.GrantType {
case "authorization_code":
access, refresh, err := security.ExchangeToken(data.Token)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.JSON(fiber.Map{
"access_token": access,
"refresh_token": refresh,
})
case "refresh_token":
access, refresh, err := security.RefreshToken(data.Token)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.JSON(fiber.Map{
"access_token": access,
"refresh_token": refresh,
})
default:
return fiber.NewError(fiber.StatusBadRequest, "Unsupported exchange token type.")
}
}

View File

@ -1,26 +0,0 @@
package server
import (
"code.smartsheep.studio/hydrogen/bus/pkg/kit/adaptor"
"code.smartsheep.studio/hydrogen/bus/pkg/kit/publisher"
"code.smartsheep.studio/hydrogen/bus/pkg/wire"
"code.smartsheep.studio/hydrogen/passport/pkg/security"
"code.smartsheep.studio/hydrogen/passport/pkg/services"
)
func getFactorToken(c *publisher.RequestCtx) error {
data := adaptor.ParseAnyToStruct[struct {
ID uint `json:"id"`
}](c.Parameters)
factor, err := services.LookupFactor(data.ID)
if err != nil {
return c.SendError(wire.InvalidActions, err)
}
if err := security.GetFactorCode(factor); err != nil {
return c.SendError(wire.InvalidActions, err)
}
return c.SendResponse(nil)
}

22
pkg/server/factors_api.go Normal file
View File

@ -0,0 +1,22 @@
package server
import (
"code.smartsheep.studio/hydrogen/passport/pkg/security"
"code.smartsheep.studio/hydrogen/passport/pkg/services"
"github.com/gofiber/fiber/v2"
)
func requestFactorToken(c *fiber.Ctx) error {
id, _ := c.ParamsInt("factorId", 0)
factor, err := services.LookupFactor(uint(id))
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
if err := security.GetFactorCode(factor); err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
return c.SendStatus(fiber.StatusOK)
}

View File

@ -1,66 +0,0 @@
package server
import (
"code.smartsheep.studio/hydrogen/bus/pkg/kit/publisher"
"code.smartsheep.studio/hydrogen/bus/pkg/wire"
)
var Commands = map[string]publisher.CommandManifest{
"passport.accounts.new": {
Name: "Create a new account",
Description: "Create a new account on passport platform.",
Requirements: wire.CommandRequirements{},
Handle: doRegister,
},
"passport.auth.challenges.new": {
Name: "Create a new challenge",
Description: "Create a new challenge to get access session.",
Requirements: wire.CommandRequirements{},
Handle: startChallenge,
},
"passport.auth.challenges.do": {
Name: "Challenge a challenge",
Description: "Getting closer to get access session.",
Requirements: wire.CommandRequirements{},
Handle: doChallenge,
},
"passport.auth.factor.token": {
Name: "Get a factor token",
Description: "Get the factor token to finish the challenge.",
Requirements: wire.CommandRequirements{},
Handle: getFactorToken,
},
"passport.auth.tokens.exchange": {
Name: "Exchange a pair of token",
Description: "Use the grant token to exchange the first token pair.",
Requirements: wire.CommandRequirements{},
Handle: exchangeToken,
},
"passport.auth.tokens.refresh": {
Name: "Refresh a pair token",
Description: "Use the refresh token to refresh the token pair.",
Requirements: wire.CommandRequirements{},
Handle: refreshToken,
},
"passport.auth": {
Name: "Auth with access token.",
Description: "Auth gateway request with access token.",
Requirements: wire.CommandRequirements{},
Providers: []wire.CommandProvider{
{
ID: "passport.auth",
Implementation: "gateway.auth",
Weight: 1000,
},
},
Handle: doAuth,
},
"passport.test": {
Name: "Test secured resource.",
Description: "Test secured resource connectivity.",
Requirements: wire.CommandRequirements{Authorized: true},
Handle: func(c *publisher.RequestCtx) error {
return c.SendResponse("You got me!")
},
},
}

View File

@ -1,39 +1,37 @@
package server
import (
"code.smartsheep.studio/hydrogen/bus/pkg/kit/publisher"
"github.com/gofiber/fiber/v2"
jsoniter "github.com/json-iterator/go"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
)
const (
Hostname = "hydrogen.passport"
Namespace = "passport"
)
var A *fiber.App
var C *publisher.PublisherConnection
func NewServer() {
A = fiber.New(fiber.Config{
DisableStartupMessage: true,
EnableIPValidation: true,
ServerHeader: "Hydrogen.Passport",
AppName: "Hydrogen.Passport",
JSONEncoder: jsoniter.ConfigCompatibleWithStandardLibrary.Marshal,
JSONDecoder: jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal,
})
func InitConnection(addr, id string) error {
if conn, err := publisher.NewConnection(
addr,
id,
Hostname,
Namespace,
viper.Get("credentials"),
); err != nil {
return err
} else {
C = conn
api := A.Group("/api").Name("API")
{
api.Post("/users", doRegister)
api.Put("/auth", startChallenge)
api.Post("/auth", doChallenge)
api.Post("/auth/token", exchangeToken)
api.Post("/auth/factors/:factorId", requestFactorToken)
}
return nil
}
func PublishCommands(conn *publisher.PublisherConnection) error {
for k, v := range Commands {
if err := conn.PublishCommand(k, v); err != nil {
return err
}
func Listen() {
if err := A.Listen(viper.GetString("bind")); err != nil {
log.Fatal().Err(err).Msg("An error occurred when starting server...")
}
return nil
}

18
pkg/server/utils.go Normal file
View File

@ -0,0 +1,18 @@
package server
import (
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
)
var validation = validator.New()
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
}