🎉 Initial Commit

This commit is contained in:
2024-01-07 01:56:32 +08:00
commit 632d614d1e
29 changed files with 1240 additions and 0 deletions

44
pkg/server/accounts.go Normal file
View File

@ -0,0 +1,44 @@
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"
)
func doRegister(c *publisher.RequestCtx) error {
data := adaptor.ParseAnyToStruct[struct {
Name string `json:"name"`
Nick string `json:"nick"`
Email string `json:"email"`
Password string `json:"password"`
}](c.Parameters)
user := models.Account{
Name: data.Name,
Nick: data.Nick,
State: models.PendingAccountState,
Factors: []models.AuthFactor{
{
Type: models.PasswordAuthFactor,
Secret: security.HashPassword(data.Password),
},
},
Contacts: []models.AccountContact{
{
Type: models.EmailAccountContact,
Content: data.Email,
VerifiedAt: nil,
},
},
}
if err := database.C.Create(&user).Error; err != nil {
return c.SendError(wire.InvalidActions, err)
}
return c.SendResponse(user)
}

116
pkg/server/challanges.go Normal file
View File

@ -0,0 +1,116 @@
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,
})
}

26
pkg/server/factors.go Normal file
View File

@ -0,0 +1,26 @@
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)
}

45
pkg/server/index.go Normal file
View File

@ -0,0 +1,45 @@
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,
},
}

39
pkg/server/startup.go Normal file
View File

@ -0,0 +1,39 @@
package server
import (
"code.smartsheep.studio/hydrogen/bus/pkg/kit/publisher"
"github.com/spf13/viper"
)
const (
Hostname = "hydrogen.passport"
Namespace = "passport"
)
var C *publisher.PublisherConnection
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
}
return nil
}
func PublishCommands(conn *publisher.PublisherConnection) error {
for k, v := range Commands {
if err := conn.PublishCommand(k, v); err != nil {
return err
}
}
return nil
}