Compare commits
No commits in common. "a3cfebab307a87bc895d7b1c1f0e6632a708562c" and "593410c1378e87409d94977d306872243fef1c1e" have entirely different histories.
a3cfebab30
...
593410c137
2
db/db.go
2
db/db.go
|
@ -53,7 +53,7 @@ func Init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automatically migrate the database schema based on the model definitions.
|
// Automatically migrate the database schema based on the model definitions.
|
||||||
err = db.Migrator().AutoMigrate(&model.Account{}, &model.Key{}, &model.KeyChallenge{}, &model.LoginSession{})
|
err = db.Migrator().AutoMigrate(&model.Account{}, &model.Key{}, &model.KeyChallenge{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("Database setup failed database type: %s \n", err))
|
panic(fmt.Errorf("Database setup failed database type: %s \n", err))
|
||||||
}
|
}
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -3,7 +3,6 @@ module git.lumeweb.com/LumeWeb/portal
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-playground/validator/v10 v10.13.0
|
|
||||||
github.com/iris-contrib/swagger v0.0.0-20230311205341-32127a753a68
|
github.com/iris-contrib/swagger v0.0.0-20230311205341-32127a753a68
|
||||||
github.com/joomcode/errorx v1.1.0
|
github.com/joomcode/errorx v1.1.0
|
||||||
github.com/kataras/iris/v12 v12.2.0
|
github.com/kataras/iris/v12 v12.2.0
|
||||||
|
@ -50,8 +49,6 @@ require (
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/spec v0.20.9 // indirect
|
github.com/go-openapi/spec v0.20.9 // indirect
|
||||||
github.com/go-openapi/swag v0.22.3 // indirect
|
github.com/go-openapi/swag v0.22.3 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
|
||||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||||
github.com/gobwas/httphead v0.1.0 // indirect
|
github.com/gobwas/httphead v0.1.0 // indirect
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
|
@ -79,7 +76,6 @@ require (
|
||||||
github.com/klauspost/compress v1.16.5 // indirect
|
github.com/klauspost/compress v1.16.5 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.11.7 // indirect
|
github.com/klauspost/reedsolomon v1.11.7 // indirect
|
||||||
github.com/leodido/go-urn v1.2.3 // indirect
|
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -150,13 +150,6 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
||||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
|
||||||
github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ=
|
|
||||||
github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
@ -323,8 +316,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||||
github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA=
|
|
||||||
github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
@ -429,7 +420,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||||
github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg=
|
github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg=
|
||||||
|
|
41
main.go
41
main.go
|
@ -6,7 +6,6 @@ import (
|
||||||
"git.lumeweb.com/LumeWeb/portal/db"
|
"git.lumeweb.com/LumeWeb/portal/db"
|
||||||
_ "git.lumeweb.com/LumeWeb/portal/docs"
|
_ "git.lumeweb.com/LumeWeb/portal/docs"
|
||||||
"git.lumeweb.com/LumeWeb/portal/service"
|
"git.lumeweb.com/LumeWeb/portal/service"
|
||||||
"git.lumeweb.com/LumeWeb/portal/validator"
|
|
||||||
"github.com/iris-contrib/swagger"
|
"github.com/iris-contrib/swagger"
|
||||||
"github.com/iris-contrib/swagger/swaggerFiles"
|
"github.com/iris-contrib/swagger/swaggerFiles"
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
|
@ -18,19 +17,19 @@ import (
|
||||||
//go:embed app/*
|
//go:embed app/*
|
||||||
var embedFrontend embed.FS
|
var embedFrontend embed.FS
|
||||||
|
|
||||||
// @title Lume Web Portal
|
// @title Lume Web Portal
|
||||||
// @version 1.0
|
// @version 1.0
|
||||||
// @description A decentralized data storage portal for the open web
|
// @description A decentralized data storage portal for the open web
|
||||||
|
|
||||||
// @contact.name Lume Web Project
|
// @contact.name Lume Web Project
|
||||||
// @contact.url https://lumeweb.com
|
// @contact.url https://lumeweb.com
|
||||||
// @contact.email contact@lumeweb.com
|
// @contact.email contact@lumeweb.com
|
||||||
|
|
||||||
// @license.name MIT
|
// @license.name MIT
|
||||||
// @license.url https://opensource.org/license/mit/
|
// @license.url https://opensource.org/license/mit/
|
||||||
|
|
||||||
// @externalDocs.description OpenAPI
|
// @externalDocs.description OpenAPI
|
||||||
// @externalDocs.url https://swagger.io/resources/open-api/
|
// @externalDocs.url https://swagger.io/resources/open-api/
|
||||||
func main() {
|
func main() {
|
||||||
// Initialize the configuration settings
|
// Initialize the configuration settings
|
||||||
config.Init()
|
config.Init()
|
||||||
|
@ -41,27 +40,22 @@ func main() {
|
||||||
// Create a new Iris app instance
|
// Create a new Iris app instance
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
|
|
||||||
app.Validator = validator.Get()
|
|
||||||
|
|
||||||
// Enable Gzip compression for responses
|
// Enable Gzip compression for responses
|
||||||
app.Use(iris.Compression)
|
app.Use(iris.Compression)
|
||||||
|
|
||||||
// Serve static files from the embedded directory at the app's root path
|
// Serve static files from the embedded directory at the app's root path
|
||||||
app.HandleDir("/", embedFrontend)
|
app.HandleDir("/", embedFrontend)
|
||||||
|
|
||||||
api := app.Party("/api")
|
|
||||||
v1 := api.Party("/v1")
|
|
||||||
|
|
||||||
// Register the AccountService with the MVC framework and attach it to the "/api/account" path
|
// Register the AccountService with the MVC framework and attach it to the "/api/account" path
|
||||||
mvc.Configure(v1.Party("/account"), func(app *mvc.Application) {
|
mvc.Configure(app.Party("/api/account"), func(app *mvc.Application) {
|
||||||
app.Handle(new(service.AccountService))
|
app.Handle(new(service.AccountService))
|
||||||
})
|
})
|
||||||
|
|
||||||
mvc.Configure(v1.Party("/auth"), func(app *mvc.Application) {
|
mvc.Configure(app.Party("/api/auth"), func(app *mvc.Application) {
|
||||||
app.Handle(new(service.AuthService))
|
app.Handle(new(service.AuthService))
|
||||||
})
|
})
|
||||||
|
|
||||||
swaggerConfig := swagger.Config{
|
config := swagger.Config{
|
||||||
// The url pointing to API definition.
|
// The url pointing to API definition.
|
||||||
URL: "http://localhost:8080/swagger/doc.json",
|
URL: "http://localhost:8080/swagger/doc.json",
|
||||||
DeepLinking: true,
|
DeepLinking: true,
|
||||||
|
@ -70,7 +64,7 @@ func main() {
|
||||||
// The UI prefix URL (see route).
|
// The UI prefix URL (see route).
|
||||||
Prefix: "/swagger",
|
Prefix: "/swagger",
|
||||||
}
|
}
|
||||||
swaggerUI := swagger.Handler(swaggerFiles.Handler, swaggerConfig)
|
swaggerUI := swagger.Handler(swaggerFiles.Handler, config)
|
||||||
|
|
||||||
app.Get("/swagger", swaggerUI)
|
app.Get("/swagger", swaggerUI)
|
||||||
// And the wildcard one for index.html, *.js, *.css and e.t.c.
|
// And the wildcard one for index.html, *.js, *.css and e.t.c.
|
||||||
|
@ -80,10 +74,5 @@ func main() {
|
||||||
//go renterd.Main()
|
//go renterd.Main()
|
||||||
|
|
||||||
// Start the Iris app and listen for incoming requests on port 80
|
// Start the Iris app and listen for incoming requests on port 80
|
||||||
log.Fatal(app.Listen(":8080", func(app *iris.Application) {
|
log.Fatal(app.Listen(":80"))
|
||||||
routes := app.GetRoutes()
|
|
||||||
for _, route := range routes {
|
|
||||||
log.Println(route)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
13
model/key.go
13
model/key.go
|
@ -7,10 +7,11 @@ import (
|
||||||
|
|
||||||
type Key struct {
|
type Key struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
ID uint `gorm:"primaryKey"`
|
ID uint `gorm:"primaryKey"`
|
||||||
AccountID uint
|
AccountID uint
|
||||||
Account Account
|
Account Account
|
||||||
Pubkey string
|
PublicKey string
|
||||||
CreatedAt time.Time
|
PrivateKey string
|
||||||
UpdatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +1,22 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"git.lumeweb.com/LumeWeb/portal/db"
|
"git.lumeweb.com/LumeWeb/portal/db"
|
||||||
"git.lumeweb.com/LumeWeb/portal/model"
|
"git.lumeweb.com/LumeWeb/portal/model"
|
||||||
_validator "git.lumeweb.com/LumeWeb/portal/validator"
|
|
||||||
"github.com/go-playground/validator/v10"
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"reflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AccountService struct {
|
type AccountService struct {
|
||||||
Ctx iris.Context
|
ctx iris.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegisterRequest struct {
|
type RegisterRequest struct {
|
||||||
Email string `json:"email" validate:"required"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Pubkey string `json:"pubkey"`
|
Pubkey []byte `json:"pubkey"`
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
jsonValidator := _validator.Get()
|
|
||||||
|
|
||||||
jsonValidator.RegisterStructValidation(ValidateRegisterRequest, RegisterRequest{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateRegisterRequest(structLevel validator.StructLevel) {
|
|
||||||
|
|
||||||
request := structLevel.Current().Interface().(RegisterRequest)
|
|
||||||
|
|
||||||
pubkey := len(request.Pubkey) == 0
|
|
||||||
pass := len(request.Password) == 0
|
|
||||||
|
|
||||||
if pubkey == pass {
|
|
||||||
structLevel.ReportError(reflect.ValueOf(request.Email), "email", "Email", "emailorpubkey", "")
|
|
||||||
structLevel.ReportError(reflect.ValueOf(request.Pubkey), "pubkey", "Pubkey", "emailorpubkey", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !pubkey {
|
|
||||||
pubkeyBytes, err := hex.DecodeString(request.Pubkey)
|
|
||||||
if err != nil || len(pubkeyBytes) != ed25519.PublicKeySize {
|
|
||||||
structLevel.ReportError(reflect.ValueOf(request.Pubkey), "pubkey", "Pubkey", "pubkey", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashPassword(password string) (string, error) {
|
func hashPassword(password string) (string, error) {
|
||||||
|
@ -67,62 +34,46 @@ func hashPassword(password string) (string, error) {
|
||||||
func (a *AccountService) PostRegister() {
|
func (a *AccountService) PostRegister() {
|
||||||
var r RegisterRequest
|
var r RegisterRequest
|
||||||
|
|
||||||
if err := a.Ctx.ReadJSON(&r); err != nil {
|
if err := a.ctx.ReadJSON(&r); err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, err)
|
a.ctx.StopWithError(iris.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the password before saving it to the database.
|
||||||
|
hashedPassword, err := hashPassword(r.Password)
|
||||||
|
if err != nil {
|
||||||
|
a.ctx.StopWithError(iris.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if an account with the same email address already exists.
|
// Check if an account with the same email address already exists.
|
||||||
existingAccount := model.Account{}
|
existingAccount := model.Account{}
|
||||||
err := db.Get().Where("email = ?", r.Email).First(&existingAccount).Error
|
err = db.Get().Where("email = ?", r.Email).First(&existingAccount).Error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// An account with the same email address already exists.
|
// An account with the same email address already exists.
|
||||||
// Return an error response to the client.
|
// Return an error response to the client.
|
||||||
a.Ctx.StopWithError(iris.StatusConflict, errors.New("an account with this email address already exists"))
|
a.ctx.StopWithError(iris.StatusConflict, errors.New("an account with this email address already exists"))
|
||||||
return
|
return
|
||||||
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
// An unexpected error occurred while querying the database.
|
// An unexpected error occurred while querying the database.
|
||||||
// Return an error response to the client.
|
// Return an error response to the client.
|
||||||
a.Ctx.StopWithError(iris.StatusInternalServerError, err)
|
a.ctx.StopWithError(iris.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Account model with the provided email and hashed password.
|
// Create a new Account model with the provided email and hashed password.
|
||||||
account := model.Account{
|
account := model.Account{
|
||||||
Email: r.Email,
|
Email: r.Email,
|
||||||
|
Password: &hashedPassword,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash the password before saving it to the database.
|
// Save the new account to the database.
|
||||||
if len(r.Password) > 0 {
|
err = db.Get().Create(&account).Error
|
||||||
hashedPassword, err := hashPassword(r.Password)
|
|
||||||
if err != nil {
|
|
||||||
a.Ctx.StopWithError(iris.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
account.Password = &hashedPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.Get().Transaction(func(tx *gorm.DB) error {
|
|
||||||
// do some database operations in the transaction (use 'tx' from this point, not 'db')
|
|
||||||
if err := tx.Create(&account).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(r.Pubkey) > 0 {
|
|
||||||
if err := tx.Create(&model.Key{Account: account, Pubkey: r.Pubkey}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return nil will commit the whole transaction
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusInternalServerError, err)
|
a.ctx.StopWithError(iris.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a success response to the client.
|
// Return a success response to the client.
|
||||||
a.Ctx.StatusCode(iris.StatusCreated)
|
a.ctx.StatusCode(iris.StatusCreated)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthService struct {
|
type AuthService struct {
|
||||||
Ctx iris.Context
|
ctx iris.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginRequest struct {
|
type LoginRequest struct {
|
||||||
|
@ -136,101 +136,101 @@ func (a *AuthService) PostLogin() {
|
||||||
var r LoginRequest
|
var r LoginRequest
|
||||||
|
|
||||||
// Read the login request from the client.
|
// Read the login request from the client.
|
||||||
if err := a.Ctx.ReadJSON(&r); err != nil {
|
if err := a.ctx.ReadJSON(&r); err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, err)
|
a.ctx.StopWithError(iris.StatusBadRequest, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the account for the given email.
|
// Retrieve the account for the given email.
|
||||||
account := model.Account{}
|
account := model.Account{}
|
||||||
if err := db.Get().Where("email = ?", r.Email).First(&account).Error; err != nil {
|
if err := db.Get().Where("email = ?", r.Email).First(&account).Error; err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errors.New("invalid email or password"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errors.New("invalid email or password"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the provided password against the hashed password stored in the database.
|
// Verify the provided password against the hashed password stored in the database.
|
||||||
if err := verifyPassword(*account.Password, r.Password); err != nil {
|
if err := verifyPassword(*account.Password, r.Password); err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errors.New("invalid email or password"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errors.New("invalid email or password"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a JWT token for the authenticated user.
|
// Generate a JWT token for the authenticated user.
|
||||||
token, err := generateAndSaveLoginToken(account.ID, 24*time.Hour)
|
token, err := generateAndSaveLoginToken(account.ID, 24*time.Hour)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusInternalServerError, fmt.Errorf("failed to generate token: %s", err))
|
a.ctx.StopWithError(iris.StatusInternalServerError, fmt.Errorf("failed to generate token: %s", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the JWT token to the client.
|
// Return the JWT token to the client.
|
||||||
err = a.Ctx.JSON(&LoginResponse{Token: token})
|
err = a.ctx.JSON(&LoginResponse{Token: token})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("Error with login attempt: %s \n", err))
|
panic(fmt.Errorf("Error with login attempt: %s \n", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostChallenge handles the POST /api/auth/pubkey/challenge request to generate a challenge for a user's public key.
|
// PostChallenge handles the POST /api/auth/pubkey-challenge request to generate a challenge for a user's public key.
|
||||||
func (a *AuthService) PostPubkeyChallenge() {
|
func (a *AuthService) PostPubkeyChallenge() {
|
||||||
var r LoginRequest
|
var r LoginRequest
|
||||||
|
|
||||||
// Read the login request from the client.
|
// Read the login request from the client.
|
||||||
if err := a.Ctx.ReadJSON(&r); err != nil {
|
if err := a.ctx.ReadJSON(&r); err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, err)
|
a.ctx.StopWithError(iris.StatusBadRequest, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the account for the given email.
|
// Retrieve the account for the given email.
|
||||||
account := model.Account{}
|
account := model.Account{}
|
||||||
if err := db.Get().Where("email = ?", r.Email).First(&account).Error; err != nil {
|
if err := db.Get().Where("email = ?", r.Email).First(&account).Error; err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errors.New("invalid email or password"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errors.New("invalid email or password"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a random challenge string.
|
// Generate a random challenge string.
|
||||||
challenge, err := generateAndSaveChallengeToken(account.ID, time.Minute)
|
challenge, err := generateAndSaveChallengeToken(account.ID, time.Minute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusInternalServerError, errors.New("failed to generate challenge"))
|
a.ctx.StopWithError(iris.StatusInternalServerError, errors.New("failed to generate challenge"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the challenge to the client.
|
// Return the challenge to the client.
|
||||||
err = a.Ctx.JSON(&ChallengeResponse{Challenge: challenge})
|
err = a.ctx.JSON(&ChallengeResponse{Challenge: challenge})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("Error with challenge request: %s \n", err))
|
panic(fmt.Errorf("Error with challenge request: %s \n", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostKeyLogin handles the POST /api/auth/pubkey/login request to authenticate a user using a public key challenge and return a JWT token.
|
// PostKeyLogin handles the POST /api/auth/pubkey-login request to authenticate a user using a public key challenge and return a JWT token.
|
||||||
func (a *AuthService) PostPubkeyLogin() {
|
func (a *AuthService) PostPubkeyLogin() {
|
||||||
var r PubkeyLoginRequest
|
var r PubkeyLoginRequest
|
||||||
|
|
||||||
// Read the key login request from the client.
|
// Read the key login request from the client.
|
||||||
if err := a.Ctx.ReadJSON(&r); err != nil {
|
if err := a.ctx.ReadJSON(&r); err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, err)
|
a.ctx.StopWithError(iris.StatusBadRequest, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the key challenge for the given challenge.
|
// Retrieve the key challenge for the given challenge.
|
||||||
challenge := model.KeyChallenge{}
|
challenge := model.KeyChallenge{}
|
||||||
if err := db.Get().Where("challenge = ?", r.Challenge).Preload("Key").First(&challenge).Error; err != nil {
|
if err := db.Get().Where("challenge = ?", r.Challenge).Preload("Key").First(&challenge).Error; err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid key challenge"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid key challenge"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, []byte(r.Challenge), blocklist)
|
verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, []byte(r.Challenge), blocklist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid key challenge"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid key challenge"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rawPubKey, err := hex.DecodeString(r.Pubkey)
|
rawPubKey, err := hex.DecodeString(r.Pubkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid pubkey"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid pubkey"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rawSignature, err := hex.DecodeString(r.Signature)
|
rawSignature, err := hex.DecodeString(r.Signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid signature"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid signature"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,29 +238,29 @@ func (a *AuthService) PostPubkeyLogin() {
|
||||||
|
|
||||||
// Verify the challenge signature.
|
// Verify the challenge signature.
|
||||||
if !ed25519.Verify(publicKeyDecoded, []byte(r.Challenge), rawSignature) {
|
if !ed25519.Verify(publicKeyDecoded, []byte(r.Challenge), rawSignature) {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid challenge"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("invalid challenge"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a JWT token for the authenticated user.
|
// Generate a JWT token for the authenticated user.
|
||||||
token, err := generateAndSaveLoginToken(challenge.AccountID, 24*time.Hour)
|
token, err := generateAndSaveLoginToken(challenge.AccountID, 24*time.Hour)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusInternalServerError, errorx.RejectedOperation.Wrap(err, "failed to generate token"))
|
a.ctx.StopWithError(iris.StatusInternalServerError, errorx.RejectedOperation.Wrap(err, "failed to generate token"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = blocklist.InvalidateToken(verifiedToken.Token, verifiedToken.StandardClaims)
|
err = blocklist.InvalidateToken(verifiedToken.Token, verifiedToken.StandardClaims)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusInternalServerError, errorx.RejectedOperation.Wrap(err, "failed to invalidate token"))
|
a.ctx.StopWithError(iris.StatusInternalServerError, errorx.RejectedOperation.Wrap(err, "failed to invalidate token"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.Get().Delete(&challenge).Error; err != nil {
|
if err := db.Get().Delete(&challenge).Error; err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("failed to delete key challenge"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errorx.RejectedOperation.New("failed to delete key challenge"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the JWT token to the client.
|
// Return the JWT token to the client.
|
||||||
err = a.Ctx.JSON(&LoginResponse{Token: token})
|
err = a.ctx.JSON(&LoginResponse{Token: token})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("Error with login attempt: %s \n", err))
|
panic(fmt.Errorf("Error with login attempt: %s \n", err))
|
||||||
}
|
}
|
||||||
|
@ -272,15 +272,15 @@ func (a *AuthService) PostLogout() {
|
||||||
var r LogoutRequest
|
var r LogoutRequest
|
||||||
|
|
||||||
// Read the logout request from the client.
|
// Read the logout request from the client.
|
||||||
if err := a.Ctx.ReadJSON(&r); err != nil {
|
if err := a.ctx.ReadJSON(&r); err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, err)
|
a.ctx.StopWithError(iris.StatusBadRequest, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the provided token.
|
// Verify the provided token.
|
||||||
claims, err := jwt.Verify(jwt.HS256, sharedKey, []byte(r.Token), blocklist)
|
claims, err := jwt.Verify(jwt.HS256, sharedKey, []byte(r.Token), blocklist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.Ctx.StopWithError(iris.StatusBadRequest, errors.New("invalid token"))
|
a.ctx.StopWithError(iris.StatusBadRequest, errors.New("invalid token"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,5 +290,5 @@ func (a *AuthService) PostLogout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a success response to the client.
|
// Return a success response to the client.
|
||||||
a.Ctx.StatusCode(iris.StatusNoContent)
|
a.ctx.StatusCode(iris.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package validator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-playground/validator/v10"
|
|
||||||
)
|
|
||||||
|
|
||||||
var v *validator.Validate
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
v = validator.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Get() *validator.Validate {
|
|
||||||
return v
|
|
||||||
}
|
|
Loading…
Reference in New Issue