🗑️ Remove bus deps and code
This commit is contained in:
@ -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)
|
||||
}
|
@ -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",
|
||||
})
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
124
pkg/server/challanges_api.go
Normal file
124
pkg/server/challanges_api.go
Normal 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.")
|
||||
}
|
||||
}
|
@ -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
22
pkg/server/factors_api.go
Normal 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)
|
||||
}
|
@ -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!")
|
||||
},
|
||||
},
|
||||
}
|
@ -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
18
pkg/server/utils.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user