From 13843dafd32e55d389d5cb205fedacc4c616600b Mon Sep 17 00:00:00 2001 From: p0t4t0sandwich Date: Fri, 19 Apr 2024 05:34:59 -0600 Subject: [PATCH] Reorganized auth module --- middleware/middleware.go | 4 +-- modules/auth/linking/discord.go | 3 +- modules/auth/{ => permissions}/rbac.go | 2 +- modules/auth/routes/authroutes.go | 11 +++--- .../{sessionservice.go => session/service.go} | 2 +- .../{sessionsstore.go => session/store.go} | 2 +- modules/auth/session/types.go | 36 +++++++++++++++++++ modules/auth/{accountstore.go => store.go} | 0 modules/auth/types.go | 36 +++---------------- modules/bee_name_generator/handler.go | 23 ++++++------ modules/pet_pictures/handler.go | 19 +++++----- 11 files changed, 76 insertions(+), 62 deletions(-) rename modules/auth/{ => permissions}/rbac.go (98%) rename modules/auth/{sessionservice.go => session/service.go} (99%) rename modules/auth/{sessionsstore.go => session/store.go} (99%) create mode 100644 modules/auth/session/types.go rename modules/auth/{accountstore.go => store.go} (100%) diff --git a/middleware/middleware.go b/middleware/middleware.go index 533fe64..d52cf9d 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/NeuralNexusDev/neuralnexus-api/modules/auth" + sess "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/session" "github.com/NeuralNexusDev/neuralnexus-api/modules/database" "github.com/NeuralNexusDev/neuralnexus-api/responses" "github.com/google/uuid" @@ -70,7 +70,7 @@ func Auth(next http.HandlerFunc) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { db := database.GetDB("neuralnexus") rdb := database.GetRedis() - sessService := auth.NewSessionService(auth.NewSessionStore(db, rdb)) + sessService := sess.NewSessionService(sess.NewSessionStore(db, rdb)) authHeader := r.Header.Get("Authorization") if authHeader == "" { diff --git a/modules/auth/linking/discord.go b/modules/auth/linking/discord.go index e5bfab4..7143bd1 100644 --- a/modules/auth/linking/discord.go +++ b/modules/auth/linking/discord.go @@ -11,6 +11,7 @@ import ( "time" "github.com/NeuralNexusDev/neuralnexus-api/modules/auth" + sess "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/session" "github.com/google/uuid" ) @@ -203,7 +204,7 @@ func GetDiscordUser(accessToken string) (*DiscordData, error) { } // DiscordOAuth process the Discord OAuth flow -func DiscordOAuth(as auth.AccountStore, ss auth.SessionStore, las LinkAccountStore, code, state string) (*auth.Session, error) { +func DiscordOAuth(as auth.AccountStore, ss sess.SessionStore, las LinkAccountStore, code, state string) (*sess.Session, error) { var a *auth.Account // TODO: Sign the state so it can't be tampered with/impersonated if state != "" && false { // TEMPORARILY DISABLED diff --git a/modules/auth/rbac.go b/modules/auth/permissions/rbac.go similarity index 98% rename from modules/auth/rbac.go rename to modules/auth/permissions/rbac.go index 3738a4f..85e73e7 100644 --- a/modules/auth/rbac.go +++ b/modules/auth/permissions/rbac.go @@ -1,4 +1,4 @@ -package auth +package perms import "errors" diff --git a/modules/auth/routes/authroutes.go b/modules/auth/routes/authroutes.go index 52ea19a..d0d05f7 100644 --- a/modules/auth/routes/authroutes.go +++ b/modules/auth/routes/authroutes.go @@ -8,6 +8,7 @@ import ( mw "github.com/NeuralNexusDev/neuralnexus-api/middleware" "github.com/NeuralNexusDev/neuralnexus-api/modules/auth" accountlinking "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/linking" + sess "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/session" "github.com/NeuralNexusDev/neuralnexus-api/modules/database" "github.com/NeuralNexusDev/neuralnexus-api/responses" ) @@ -19,7 +20,7 @@ func ApplyRoutes(mux *http.ServeMux) *http.ServeMux { db := database.GetDB("neuralnexus") rdb := database.GetRedis() acctStore := auth.NewAccountStore(db) - sessStore := auth.NewSessionStore(db, rdb) + sessStore := sess.NewSessionStore(db, rdb) alstore := accountlinking.NewStore(db) mux.HandleFunc("POST /api/v1/auth/login", LoginHandler(acctStore, sessStore)) @@ -30,7 +31,7 @@ func ApplyRoutes(mux *http.ServeMux) *http.ServeMux { } // LoginHandler handles the login route -func LoginHandler(as auth.AccountStore, ss auth.SessionStore) http.HandlerFunc { +func LoginHandler(as auth.AccountStore, ss sess.SessionStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var login struct { Username string `json:"username" xml:"username" validate:"required_without=Email"` @@ -67,9 +68,9 @@ func LoginHandler(as auth.AccountStore, ss auth.SessionStore) http.HandlerFunc { } // LogoutHandler handles the logout route -func LogoutHandler(ss auth.SessionStore) http.HandlerFunc { +func LogoutHandler(ss sess.SessionStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value(mw.SessionKey).(*auth.Session) + session := r.Context().Value(mw.SessionKey).(*sess.Session) ss.DeleteSessionFromCache(session.ID) responses.SendAndEncodeStruct(w, r, http.StatusOK, session) ss.DeleteSessionInDB(session.ID) @@ -77,7 +78,7 @@ func LogoutHandler(ss auth.SessionStore) http.HandlerFunc { } // OAuthHandler handles the Discord OAuth route -func OAuthHandler(as auth.AccountStore, ss auth.SessionStore, las accountlinking.LinkAccountStore) http.HandlerFunc { +func OAuthHandler(as auth.AccountStore, ss sess.SessionStore, las accountlinking.LinkAccountStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { code := r.URL.Query().Get("code") if code == "" { diff --git a/modules/auth/sessionservice.go b/modules/auth/session/service.go similarity index 99% rename from modules/auth/sessionservice.go rename to modules/auth/session/service.go index 70ddf47..625b1ff 100644 --- a/modules/auth/sessionservice.go +++ b/modules/auth/session/service.go @@ -1,4 +1,4 @@ -package auth +package sess import "github.com/google/uuid" diff --git a/modules/auth/sessionsstore.go b/modules/auth/session/store.go similarity index 99% rename from modules/auth/sessionsstore.go rename to modules/auth/session/store.go index 6d39bc2..f9cd63e 100644 --- a/modules/auth/sessionsstore.go +++ b/modules/auth/session/store.go @@ -1,4 +1,4 @@ -package auth +package sess import ( "context" diff --git a/modules/auth/session/types.go b/modules/auth/session/types.go new file mode 100644 index 0000000..9b1b3a1 --- /dev/null +++ b/modules/auth/session/types.go @@ -0,0 +1,36 @@ +package sess + +import ( + "time" + + perms "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/permissions" + "github.com/google/uuid" +) + +// Session struct +type Session struct { + ID uuid.UUID `json:"session_id" xml:"session_id" db:"session_id"` + UserID uuid.UUID `json:"user_id" xml:"user_id" db:"user_id"` + Permissions []string `json:"permissions" xml:"permissions" db:"permissions"` + IssuedAt int64 `json:"iat" xml:"iat" db:"iat"` + LastUsedAt int64 `json:"lua" xml:"lua" db:"lua"` + ExpiresAt int64 `json:"exp" xml:"exp" db:"exp"` +} + +// HasPermission checks if a session has a permission +func (s *Session) HasPermission(permission perms.Scope) bool { + for _, p := range s.Permissions { + if p == permission.Name+"|"+permission.Value { + return true + } + } + return false +} + +// IsExpired checks if a session is expired +func (s *Session) IsValid() bool { + if s.ExpiresAt == 0 { + return true + } + return time.Now().Unix() < s.ExpiresAt +} diff --git a/modules/auth/accountstore.go b/modules/auth/store.go similarity index 100% rename from modules/auth/accountstore.go rename to modules/auth/store.go diff --git a/modules/auth/types.go b/modules/auth/types.go index 965a011..f84f3fe 100644 --- a/modules/auth/types.go +++ b/modules/auth/types.go @@ -5,6 +5,8 @@ import ( "log" "time" + perms "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/permissions" + sess "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/session" "github.com/google/uuid" "golang.org/x/crypto/argon2" ) @@ -80,21 +82,11 @@ func (user *Account) RemoveRole(role string) { } } -// Session struct -type Session struct { - ID uuid.UUID `json:"session_id" xml:"session_id" db:"session_id"` - UserID uuid.UUID `json:"user_id" xml:"user_id" db:"user_id"` - Permissions []string `json:"permissions" xml:"permissions" db:"permissions"` - IssuedAt int64 `json:"iat" xml:"iat" db:"iat"` - LastUsedAt int64 `json:"lua" xml:"lua" db:"lua"` - ExpiresAt int64 `json:"exp" xml:"exp" db:"exp"` -} - // NewSession creates a new session -func (a *Account) NewSession(expiresAt int64) *Session { +func (a *Account) NewSession(expiresAt int64) *sess.Session { permissions := []string{} for _, r := range a.Roles { - role, err := GetRoleByName(r) + role, err := perms.GetRoleByName(r) if err != nil { log.Println(err) continue @@ -104,7 +96,7 @@ func (a *Account) NewSession(expiresAt int64) *Session { } } - return &Session{ + return &sess.Session{ ID: uuid.New(), UserID: a.UserID, Permissions: permissions, @@ -113,21 +105,3 @@ func (a *Account) NewSession(expiresAt int64) *Session { ExpiresAt: expiresAt, } } - -// HasPermission checks if a session has a permission -func (s *Session) HasPermission(permission Scope) bool { - for _, p := range s.Permissions { - if p == permission.Name+"|"+permission.Value { - return true - } - } - return false -} - -// IsExpired checks if a session is expired -func (s *Session) IsValid() bool { - if s.ExpiresAt == 0 { - return true - } - return time.Now().Unix() < s.ExpiresAt -} diff --git a/modules/bee_name_generator/handler.go b/modules/bee_name_generator/handler.go index 403dbef..c3871b5 100644 --- a/modules/bee_name_generator/handler.go +++ b/modules/bee_name_generator/handler.go @@ -6,7 +6,8 @@ import ( "strconv" mw "github.com/NeuralNexusDev/neuralnexus-api/middleware" - "github.com/NeuralNexusDev/neuralnexus-api/modules/auth" + perms "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/permissions" + sess "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/session" "github.com/NeuralNexusDev/neuralnexus-api/modules/database" "github.com/NeuralNexusDev/neuralnexus-api/responses" ) @@ -41,8 +42,8 @@ func GetBeeNameHandler(s BNGStore) http.HandlerFunc { // UploadBeeNameHandler Upload a bee name func UploadBeeNameHandler(s BNGStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value(mw.SessionKey).(*auth.Session) - if !session.HasPermission(auth.ScopeAdminBeeNameGenerator) { + session := r.Context().Value(mw.SessionKey).(*sess.Session) + if !session.HasPermission(perms.ScopeAdminBeeNameGenerator) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to upload bee names") return } @@ -66,8 +67,8 @@ func UploadBeeNameHandler(s BNGStore) http.HandlerFunc { // DeleteBeeName Delete a bee name func DeleteBeeNameHandler(s BNGStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value(mw.SessionKey).(*auth.Session) - if !session.HasPermission(auth.ScopeAdminBeeNameGenerator) { + session := r.Context().Value(mw.SessionKey).(*sess.Session) + if !session.HasPermission(perms.ScopeAdminBeeNameGenerator) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to delete bee names") return } @@ -110,8 +111,8 @@ func SubmitBeeNameHandler(s BNGStore) http.HandlerFunc { // GetBeeNameSuggestions Get a list of bee name suggestions func GetBeeNameSuggestionsHandler(s BNGStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value(mw.SessionKey).(*auth.Session) - if !session.HasPermission(auth.ScopeAdminBeeNameGenerator) { + session := r.Context().Value(mw.SessionKey).(*sess.Session) + if !session.HasPermission(perms.ScopeAdminBeeNameGenerator) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to get bee name suggestions") return } @@ -143,8 +144,8 @@ func GetBeeNameSuggestionsHandler(s BNGStore) http.HandlerFunc { // AcceptBeeNameSuggestionHandler Accept a bee name suggestion func AcceptBeeNameSuggestionHandler(s BNGStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value(mw.SessionKey).(*auth.Session) - if !session.HasPermission(auth.ScopeAdminBeeNameGenerator) { + session := r.Context().Value(mw.SessionKey).(*sess.Session) + if !session.HasPermission(perms.ScopeAdminBeeNameGenerator) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to accept bee name suggestions") return } @@ -168,8 +169,8 @@ func AcceptBeeNameSuggestionHandler(s BNGStore) http.HandlerFunc { // RejectBeeNameSuggestionHandler Reject a bee name suggestion func RejectBeeNameSuggestionHandler(s BNGStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value(mw.SessionKey).(*auth.Session) - if !session.HasPermission(auth.ScopeAdminBeeNameGenerator) { + session := r.Context().Value(mw.SessionKey).(*sess.Session) + if !session.HasPermission(perms.ScopeAdminBeeNameGenerator) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to reject bee name suggestions") return } diff --git a/modules/pet_pictures/handler.go b/modules/pet_pictures/handler.go index e8179f9..7c9ed0d 100644 --- a/modules/pet_pictures/handler.go +++ b/modules/pet_pictures/handler.go @@ -6,7 +6,8 @@ import ( "strconv" mw "github.com/NeuralNexusDev/neuralnexus-api/middleware" - "github.com/NeuralNexusDev/neuralnexus-api/modules/auth" + perms "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/permissions" + sess "github.com/NeuralNexusDev/neuralnexus-api/modules/auth/session" "github.com/NeuralNexusDev/neuralnexus-api/modules/database" "github.com/NeuralNexusDev/neuralnexus-api/responses" ) @@ -32,8 +33,8 @@ func ApplyRoutes(router *http.ServeMux) *http.ServeMux { // CreatePetHandler - Create a new pet func CreatePetHandler(s PetPicService) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value(mw.SessionKey).(auth.Session) - if !session.HasPermission(auth.ScopeAdminPetPictures) { + session := r.Context().Value(mw.SessionKey).(sess.Session) + if !session.HasPermission(perms.ScopeAdminPetPictures) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to create a pet") return } @@ -105,8 +106,8 @@ func UpdatePetHandler(s PetPicService) http.HandlerFunc { return } - session := r.Context().Value(mw.SessionKey).(auth.Session) - if !session.HasPermission(auth.ScopePetPictures(pet.Name)) { + session := r.Context().Value(mw.SessionKey).(sess.Session) + if !session.HasPermission(perms.ScopePetPictures(pet.Name)) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to update this pet") return } @@ -190,8 +191,8 @@ func UpdatePetPictureHandler(s PetPicService) http.HandlerFunc { return } - session := r.Context().Value(mw.SessionKey).(auth.Session) - if !session.HasPermission(auth.ScopePetPictures(pet.Name)) { + session := r.Context().Value(mw.SessionKey).(sess.Session) + if !session.HasPermission(perms.ScopePetPictures(pet.Name)) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to update this pet") return } @@ -236,8 +237,8 @@ func DeletePetPictureHandler(s PetPicService) http.HandlerFunc { return } - session := r.Context().Value(mw.SessionKey).(auth.Session) - if !session.HasPermission(auth.ScopePetPictures(pet.Name)) { + session := r.Context().Value(mw.SessionKey).(sess.Session) + if !session.HasPermission(perms.ScopePetPictures(pet.Name)) { responses.SendAndEncodeForbidden(w, r, "You do not have permission to update this pet") return }