wtf auth??
This commit is contained in:
parent
607d7e121c
commit
a3a1654030
14 changed files with 337 additions and 1 deletions
130
authmiddleware/authmiddleware.go
Normal file
130
authmiddleware/authmiddleware.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package authmiddleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"slices"
|
||||
|
||||
"git.sapphic.engineer/roleypoly/v4/discord"
|
||||
"git.sapphic.engineer/roleypoly/v4/types"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gofiber/fiber/v3/middleware/session"
|
||||
)
|
||||
|
||||
type AuthMiddleware struct {
|
||||
Client discord.IDiscordClient
|
||||
|
||||
supportIDs []string
|
||||
superuserIDs []string
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
Permissions Permission
|
||||
User *types.DiscordUser
|
||||
AccessToken string
|
||||
LastRefresh time.Time
|
||||
}
|
||||
|
||||
var SessionKey uint8
|
||||
|
||||
func New(discordClient discord.IDiscordClient, supportIDs []string, superuserIDs []string) func(fiber.Ctx) error {
|
||||
am := AuthMiddleware{
|
||||
Client: discordClient,
|
||||
supportIDs: supportIDs,
|
||||
superuserIDs: superuserIDs,
|
||||
}
|
||||
|
||||
return am.Handle
|
||||
}
|
||||
|
||||
var DefaultSession *Session = &Session{Permissions: PermAnonymous}
|
||||
|
||||
func (am *AuthMiddleware) Handle(c fiber.Ctx) error {
|
||||
sc := session.FromContext(c)
|
||||
sess := am.Init(sc)
|
||||
|
||||
am.validateAccessToken(sess) // this will remove AccessToken if its no good
|
||||
am.setPermissions(sess) // this captures this
|
||||
am.Commit(sc, sess) // and save our first bits?
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func (am *AuthMiddleware) Init(sess *session.Middleware) *Session {
|
||||
session, ok := sess.Get(SessionKey).(*Session)
|
||||
if !ok {
|
||||
am.Commit(sess, DefaultSession)
|
||||
return DefaultSession
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
func (am *AuthMiddleware) Commit(sc *session.Middleware, sess *Session) {
|
||||
sc.Set(SessionKey, sess)
|
||||
}
|
||||
|
||||
func SessionFrom(c fiber.Ctx) (s *Session) {
|
||||
sess := session.FromContext(c)
|
||||
s, _ = sess.Get(SessionKey).(*Session)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (am *AuthMiddleware) isSupport(userID string) bool {
|
||||
return slices.Contains(am.supportIDs, userID)
|
||||
}
|
||||
|
||||
func (am *AuthMiddleware) isSuperuser(userID string) bool {
|
||||
return slices.Contains(am.superuserIDs, userID)
|
||||
}
|
||||
|
||||
func (am *AuthMiddleware) setPermissions(sess *Session) {
|
||||
sess.Permissions = PermAnonymous
|
||||
|
||||
if sess.AccessToken != "" {
|
||||
sess.Permissions = PermUser
|
||||
}
|
||||
|
||||
if am.isSupport(sess.User.ID) {
|
||||
sess.Permissions = PermSupport
|
||||
}
|
||||
|
||||
if am.isSuperuser(sess.User.ID) {
|
||||
sess.Permissions = PermSuperuser
|
||||
}
|
||||
}
|
||||
|
||||
func (am *AuthMiddleware) validateAccessToken(sess *Session) {
|
||||
if sess.AccessToken == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if sess.LastRefresh.Add(time.Hour).Before(time.Now()) {
|
||||
user, err := am.GetCurrentUser(sess.AccessToken)
|
||||
if err != nil {
|
||||
if errors.Is(err, discord.ErrUnauthorized) {
|
||||
sess.AccessToken = ""
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sess.User = user
|
||||
}
|
||||
}
|
||||
|
||||
func (am *AuthMiddleware) GetCurrentUser(accessToken string) (*types.DiscordUser, error) {
|
||||
req := discord.NewRequest("GET", "/users/@me")
|
||||
am.Client.ClientAuth(req, accessToken)
|
||||
|
||||
resp, err := am.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("authmiddleware.GetCurrentUser: request failed: %w", err)
|
||||
}
|
||||
|
||||
var user types.DiscordUser
|
||||
err = discord.OutputResponse(resp, &user)
|
||||
return &user, err
|
||||
}
|
80
authmiddleware/authmiddleware_test.go
Normal file
80
authmiddleware/authmiddleware_test.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package authmiddleware_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"git.sapphic.engineer/roleypoly/v4/authmiddleware"
|
||||
"git.sapphic.engineer/roleypoly/v4/discord"
|
||||
"git.sapphic.engineer/roleypoly/v4/discord/clientmock"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gofiber/fiber/v3/middleware/session"
|
||||
)
|
||||
|
||||
func TestAnonymous(t *testing.T) {
|
||||
dc := clientmock.NewDiscordClientMock()
|
||||
app := getApp(dc)
|
||||
|
||||
setSession(app, dc, authmiddleware.Session{
|
||||
Permissions: authmiddleware.PermAnonymous,
|
||||
})
|
||||
}
|
||||
|
||||
func getApp(dc discord.IDiscordClient) *fiber.App {
|
||||
app := fiber.New(fiber.Config{})
|
||||
sessionMiddleware, sessionStore := session.NewWithStore()
|
||||
sessionStore.RegisterType(authmiddleware.Session{})
|
||||
|
||||
app.Use(sessionMiddleware, authmiddleware.New(dc, []string{}, []string{}))
|
||||
app.Get("/", authState)
|
||||
app.Post("/updateSession", updateSession)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func setSession(app *fiber.App, dc *clientmock.DiscordClientMock, sess authmiddleware.Session) error {
|
||||
body := bytes.Buffer{}
|
||||
json.NewEncoder(&body).Encode(sess)
|
||||
|
||||
// do mocks here
|
||||
|
||||
req, _ := http.NewRequest("POST", "/updateSession", &body)
|
||||
_, err := app.Test(req)
|
||||
return err
|
||||
}
|
||||
|
||||
func authState(c fiber.Ctx) error {
|
||||
s := authmiddleware.SessionFrom(c)
|
||||
|
||||
permList := []string{}
|
||||
|
||||
if s.Permissions >= authmiddleware.PermAnonymous {
|
||||
permList = append(permList, "anonymous")
|
||||
}
|
||||
|
||||
if s.Permissions >= authmiddleware.PermUser {
|
||||
permList = append(permList, "user")
|
||||
}
|
||||
|
||||
if s.Permissions >= authmiddleware.PermSupport {
|
||||
permList = append(permList, "support")
|
||||
}
|
||||
|
||||
if s.Permissions >= authmiddleware.PermSuperuser {
|
||||
permList = append(permList, "superuser")
|
||||
}
|
||||
|
||||
return c.JSON(permList)
|
||||
}
|
||||
|
||||
func updateSession(c fiber.Ctx) error {
|
||||
var newSession *authmiddleware.Session
|
||||
c.Bind().JSON(&newSession)
|
||||
|
||||
sc := session.FromContext(c)
|
||||
sc.Set(authmiddleware.SessionKey, newSession)
|
||||
|
||||
return c.SendString("ok")
|
||||
}
|
10
authmiddleware/const.go
Normal file
10
authmiddleware/const.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package authmiddleware
|
||||
|
||||
type Permission uint8
|
||||
|
||||
const (
|
||||
PermAnonymous Permission = 1 << iota
|
||||
PermUser
|
||||
PermSupport
|
||||
PermSuperuser
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue