From 1f08b097303a1f3e7eeef5e324c96f59aa83dcc7 Mon Sep 17 00:00:00 2001 From: Christian Roessner Date: Thu, 21 Nov 2024 13:37:57 +0100 Subject: [PATCH] Feat: Refactor authentication handling and naming conventions Renamed several functions and constants to improve code clarity around authentication. Updated comments and function logic to use more descriptive names, focusing on authentication workflows and handling across different service types. Signed-off-by: Christian Roessner --- server/core/auth.go | 52 ++++++++++++++++++------------------- server/core/features.go | 2 +- server/core/http.go | 57 +++++++++++++---------------------------- server/core/rest.go | 26 +++++++++---------- server/global/const.go | 21 +++++---------- 5 files changed, 64 insertions(+), 94 deletions(-) diff --git a/server/core/auth.go b/server/core/auth.go index f8c5fecd..5e5e7e8c 100644 --- a/server/core/auth.go +++ b/server/core/auth.go @@ -251,7 +251,7 @@ type AuthState struct { // StatusMessage is the HTTP response payload that is sent to the remote server that asked for authentication. StatusMessage string - // Service is set by Nauthilus depending on the router endpoint. Look at httpQueryHandler for the structure of available + // Service is set by Nauthilus depending on the router endpoint. Look at requestHandler for the structure of available // endpoints. Service string @@ -580,9 +580,9 @@ func (a *AuthState) authOK(ctx *gin.Context) { switch a.Service { case global.ServNginx: setNginxHeaders(ctx, a) - case global.ServDovecot: - setDovecotHeaders(ctx, a) - case global.ServUserInfo, global.ServJSON: + case global.ServHeader: + setHeaderHeaders(ctx, a) + case global.ServJSON: sendAuthResponse(ctx, a) } @@ -597,13 +597,13 @@ func (a *AuthState) authOK(ctx *gin.Context) { // setCommonHeaders sets common headers for the given gin.Context and AuthState. // It sets the "Auth-Status" header to "OK" and the "X-Nauthilus-Session" header to the GUID of the AuthState. -// If the AuthState's Service is not global.ServBasicAuth, and the HaveAccountField flag is true, +// If the AuthState's Service is not global.ServBasic, and the HaveAccountField flag is true, // it retrieves the account from the AuthState and sets the "Auth-User" header func setCommonHeaders(ctx *gin.Context, a *AuthState) { ctx.Header("Auth-Status", "OK") ctx.Header("X-Nauthilus-Session", *a.GUID) - if a.Service != global.ServBasicAuth { + if a.Service != global.ServBasic { if account, found := a.getAccountOk(); found { ctx.Header("Auth-User", account) } @@ -650,7 +650,7 @@ func setNginxHeaders(ctx *gin.Context, a *AuthState) { } } -// setDovecotHeaders sets the specified headers in the given gin.Context based on the attributes in the AuthState object. +// setHeaderHeaders sets the specified headers in the given gin.Context based on the attributes in the AuthState object. // It iterates through the attributes and calls the handleAttributeValue function for each attribute. // // Parameters: @@ -665,12 +665,12 @@ func setNginxHeaders(ctx *gin.Context, a *AuthState) { // "Attribute2": []any{"Value2_1", "Value2_2"}, // }, // } -// setDovecotHeaders(ctx, a) +// setHeaderHeaders(ctx, a) // // Resulting headers in ctx: // - X-Nauthilus-Attribute1: "Value1" // - X-Nauthilus-Attribute2: "Value2_1,Value2_2" -func setDovecotHeaders(ctx *gin.Context, a *AuthState) { +func setHeaderHeaders(ctx *gin.Context, a *AuthState) { if a.Attributes != nil && len(a.Attributes) > 0 { for name, value := range a.Attributes { handleAttributeValue(ctx, name, value) @@ -805,7 +805,7 @@ func (a *AuthState) setFailureHeaders(ctx *gin.Context) { ctx.Header("Auth-Wait", fmt.Sprintf("%v", waitDelay)) } - case global.ServUserInfo, global.ServJSON: + case global.ServJSON: ctx.Header("Content-Type", "application/json; charset=UTF-8") if a.PasswordHistory != nil { @@ -859,7 +859,7 @@ func (a *AuthState) setSMPTHeaders(ctx *gin.Context) { // // func (a *AuthState) authTempFail(ctx *gin.Context, reason string) { // ... -// if a.Service == global.ServUserInfo { +// if a.Service == global.ServJSON { // a.sendAuthResponse(ctx, reason) // return // } @@ -875,7 +875,6 @@ func (a *AuthState) setUserInfoHeaders(ctx *gin.Context, reason string) { } ctx.Header("Content-Type", "application/json; charset=UTF-8") - ctx.Header("X-User-Found", fmt.Sprintf("%v", a.UserFound)) ctx.JSON(a.StatusCodeInternalError, &errType{Error: reason}) } @@ -892,12 +891,12 @@ func (a *AuthState) setUserInfoHeaders(ctx *gin.Context, reason string) { // // Usage example: // -// func (a *AuthState) generic(ctx *gin.Context) { +// func (a *AuthState) handleAuthentication(ctx *gin.Context) { // ... // a.authTempFail(ctx, global.TempFailDefault) // ... // } -// func (a *AuthState) saslAuthd(ctx *gin.Context) { +// func (a *AuthState) handleSASLAuthdAuthentication(ctx *gin.Context) { // ... // a.authTempFail(ctx, global.TempFailDefault) // ... @@ -915,8 +914,9 @@ func (a *AuthState) authTempFail(ctx *gin.Context, reason string) { a.StatusMessage = reason - if a.Service == global.ServUserInfo { + if a.Service == global.ServJSON { a.setUserInfoHeaders(ctx, reason) + return } @@ -1117,11 +1117,11 @@ func updateAuthentication(a *AuthState, passDBResult *PassDBResult, passDB *Pass // setStatusCodes sets different status codes for various services. func (a *AuthState) setStatusCodes(service string) error { switch service { - case global.ServNginx, global.ServDovecot: + case global.ServNginx: a.StatusCodeOK = http.StatusOK a.StatusCodeInternalError = http.StatusOK a.StatusCodeFail = http.StatusOK - case global.ServSaslauthd, global.ServBasicAuth, global.ServOryHydra, global.ServUserInfo, global.ServJSON, global.ServCallback: + case global.ServSaslauthd, global.ServBasic, global.ServOryHydra, global.ServHeader, global.ServJSON, global.ServCallback: a.StatusCodeOK = http.StatusOK a.StatusCodeInternalError = http.StatusInternalServerError a.StatusCodeFail = http.StatusForbidden @@ -2163,10 +2163,10 @@ func setupBodyBasedAuth(ctx *gin.Context, auth *AuthState) { } } -// setupHTTPBasiAuth sets up basic authentication for HTTP requests. +// setupHTTPBasicAuth sets up basic authentication for HTTP requests. // It takes in a gin.Context object and a pointer to an AuthState object. // It calls the withClientInfo, withLocalInfo, withUserAgent, and withXSSL methods of the AuthState object to set client, local, user-agent, and X-SSL information, respectively -func setupHTTPBasiAuth(ctx *gin.Context, auth *AuthState) { +func setupHTTPBasicAuth(ctx *gin.Context, auth *AuthState) { // NOTE: We must get username and password later! auth.withClientInfo(ctx) auth.withLocalInfo(ctx) @@ -2192,9 +2192,9 @@ func (a *AuthState) initMethodAndUserAgent() *AuthState { // setupAuth sets up the authentication based on the service parameter in the gin context. // It takes the gin context and an AuthState struct as input. // -// If the service parameter is "nginx", "dovecot", or "user", it calls the setupHeaderBasedAuth function. +// If the service parameter is "nginx" or "header", it calls the setupHeaderBasedAuth function. // If the service parameter is "saslauthd", it calls the setupBodyBasedAuth function. -// If the service parameter is "basicauth", it calls the setupHTTPBasiAuth function. +// If the service parameter is "basicauth", it calls the setupHTTPBasicAuth function. // // After setting up the authentication, it calls the withDefaults method on the AuthState struct. // @@ -2208,19 +2208,19 @@ func setupAuth(ctx *gin.Context, auth *AuthState) { auth.Protocol = &config.Protocol{} switch ctx.Param("service") { - case global.ServNginx, global.ServDovecot, global.ServUserInfo: + case global.ServNginx, global.ServHeader: setupHeaderBasedAuth(ctx, auth) case global.ServSaslauthd, global.ServJSON: setupBodyBasedAuth(ctx, auth) - case global.ServBasicAuth: - setupHTTPBasiAuth(ctx, auth) + case global.ServBasic: + setupHTTPBasicAuth(ctx, auth) case global.ServCallback: auth.withDefaults(ctx) return } - if ctx.Query("mode") != "list-accounts" && ctx.Param("service") != global.ServBasicAuth { + if ctx.Query("mode") != "list-accounts" && ctx.Param("service") != global.ServBasic { if !util.ValidateUsername(auth.Username) { auth.Username = "" @@ -2293,7 +2293,7 @@ func (a *AuthState) withDefaults(ctx *gin.Context) *AuthState { a.Service = ctx.Param("service") a.Context = ctx.MustGet(global.CtxDataExchangeKey).(*lualib.Context) - if a.Service == global.ServBasicAuth { + if a.Service == global.ServBasic { a.Protocol.Set(global.ProtoHTTP) } diff --git a/server/core/features.go b/server/core/features.go index 322452dc..883363a7 100644 --- a/server/core/features.go +++ b/server/core/features.go @@ -47,7 +47,7 @@ func isLocalOrEmptyIP(ip string) bool { return ip == global.Localhost4 || ip == global.Localhost6 || ip == "" } -// logAddMessage logs a message with the specified parameters using the global logger. It is intended to be a generic logging function. +// logAddMessage logs a message with the specified parameters using the global logger. It is intended to be a handleAuthentication logging function. // // Parameters: // - auth: Pointer to AuthState diff --git a/server/core/http.go b/server/core/http.go index d1694f89..f1bfff54 100644 --- a/server/core/http.go +++ b/server/core/http.go @@ -69,7 +69,7 @@ var ( LangBundle *i18n.Bundle ) -// RESTResult is a generic JSON result object for the Nauthilus REST API. +// RESTResult is a handleAuthentication JSON result object for the Nauthilus REST API. type RESTResult struct { // GUID represents a unique identifier for a session. It is a string field used in the RESTResult struct // and is also annotated with the json tag "session". @@ -165,12 +165,12 @@ func (w *customWriter) Write(data []byte) (numBytes int, err error) { } //nolint:gocognit // Main logic -func httpQueryHandler(ctx *gin.Context) { +func requestHandler(ctx *gin.Context) { if ctx.FullPath() == "/ping" { healthCheck(ctx) } else { switch ctx.Param("category") { - case global.CatMail, global.CatGeneric: + case global.CatAuth: auth := NewAuthState(ctx) if auth == nil { ctx.AbortWithStatus(http.StatusBadRequest) @@ -185,42 +185,21 @@ func httpQueryHandler(ctx *gin.Context) { } switch ctx.Param("service") { - case global.ServNginx, global.ServDovecot, global.ServUserInfo, global.ServJSON: - auth.generic(ctx) + case global.ServBasic, global.ServNginx, global.ServHeader, global.ServJSON: + auth.handleAuthentication(ctx) case global.ServSaslauthd: - auth.saslAuthd(ctx) + auth.handleSASLAuthdAuthentication(ctx) case global.ServCallback: - auth.callback(ctx) + auth.handleCallback(ctx) ctx.Status(auth.StatusCodeOK) default: ctx.AbortWithStatus(http.StatusNotFound) } - case global.CatHTTP: - auth := NewAuthState(ctx) - if auth == nil { - ctx.AbortWithStatus(http.StatusBadRequest) - - return - } - - if found, reject := auth.preproccessAuthRequest(ctx); reject { - return - } else if found { - auth.withClientInfo(ctx).withLocalInfo(ctx).withUserAgent(ctx).withXSSL(ctx) - } - - switch ctx.Param("service") { - case global.ServBasicAuth: - auth.generic(ctx) - default: - ctx.AbortWithStatus(http.StatusNotFound) - } - case global.CatBruteForce: switch ctx.Param("service") { case global.ServList: - listBruteforce(ctx) + hanldeBruteForceList(ctx) default: ctx.AbortWithStatus(http.StatusNotFound) } @@ -239,23 +218,23 @@ func httpQueryHandler(ctx *gin.Context) { // 2. It uses a switch statement to handle different category values. // 3. For the "cache" category, it retrieves the "service" parameter and uses a switch statement // to handle different service values. -// 4. For the "flush" service, it calls the flushCache function. +// 4. For the "flush" service, it calls the handleUserFlush function. // 5. For the "bruteforce" category, it retrieves the "service" parameter and uses a switch statement // to handle different service values. -// 6. For the "flush" service, it calls the flushBruteForceRule function. +// 6. For the "flush" service, it calls the handleBruteForceRuleFlush function. func httpCacheHandler(ctx *gin.Context) { //nolint:gocritic // Prepared for future commands switch ctx.Param("category") { case global.CatCache: switch ctx.Param("service") { case global.ServFlush: - flushCache(ctx) + handleUserFlush(ctx) } case global.CatBruteForce: switch ctx.Param("service") { case global.ServFlush: - flushBruteForceRule(ctx) + handleBruteForceRuleFlush(ctx) } } } @@ -359,7 +338,7 @@ func basicAuthMiddleware() gin.HandlerFunc { guid := ctx.GetString(global.CtxGUIDKey) // Note: Chicken-egg problem. - if ctx.Param("category") == global.CatHTTP && ctx.Param("service") == global.ServBasicAuth { + if ctx.Param("category") == global.CatAuth && ctx.Param("service") == global.ServBasic { level.Warn(log.Logger).Log( global.LogKeyGUID, guid, global.LogKeyMsg, "Disabling HTTP basic Auth", @@ -732,8 +711,8 @@ func setupNotifyEndpoint(router *gin.Engine, sessionStore sessions.Store) { // it adds a middleware to the group that implements basic authentication. // // It then adds three endpoints to the group: -// - A GET endpoint with the path "/:category/:service" that is handled by the luaContextMiddleware and httpQueryHandler functions. -// - A POST endpoint with the path "/:category/:service" that is also handled by the luaContextMiddleware and httpQueryHandler functions. +// - A GET endpoint with the path "/:category/:service" that is handled by the luaContextMiddleware and requestHandler functions. +// - A POST endpoint with the path "/:category/:service" that is also handled by the luaContextMiddleware and requestHandler functions. // - A DELETE endpoint with the path "/:category/:service" that is handled by the httpCacheHandler function. func setupBackChannelEndpoints(router *gin.Engine) { group := router.Group("/api/v1") @@ -742,8 +721,8 @@ func setupBackChannelEndpoints(router *gin.Engine) { group.Use(basicAuthMiddleware()) } - group.GET("/:category/:service", luaContextMiddleware(), httpQueryHandler) - group.POST("/:category/:service", luaContextMiddleware(), httpQueryHandler) + group.GET("/:category/:service", luaContextMiddleware(), requestHandler) + group.POST("/:category/:service", luaContextMiddleware(), requestHandler) group.DELETE("/:category/:service", httpCacheHandler) } @@ -990,7 +969,7 @@ func setupRouter(router *gin.Engine) { router.GET("/metrics", gin.WrapF(promhttp.Handler().ServeHTTP)) // Healthcheck - router.GET("/ping", httpQueryHandler) + router.GET("/ping", requestHandler) // Parse static folder for template files router.LoadHTMLGlob(viper.GetString("html_static_content_path") + "/*.html") diff --git a/server/core/rest.go b/server/core/rest.go index 688efce8..811f477a 100644 --- a/server/core/rest.go +++ b/server/core/rest.go @@ -111,11 +111,11 @@ type FilterCmd struct { IPAddress []string `json:"ip_addresses,omitempty"` } -// generic handles the generic authentication logic based on the selected service type. -func (a *AuthState) generic(ctx *gin.Context) { +// handleAuthentication handles the authentication logic based on the selected service type. +func (a *AuthState) handleAuthentication(ctx *gin.Context) { var mode string - if a.Service == global.ServBasicAuth { + if a.Service == global.ServBasic { var httpBasicAuthOk bool // Decode HTTP basic Auth @@ -195,8 +195,8 @@ func (a *AuthState) generic(ctx *gin.Context) { } } -// saslAuthd handles the authentication logic for the saslAuthd service. -func (a *AuthState) saslAuthd(ctx *gin.Context) { +// handleSASLAuthdAuthentication handles the authentication logic for the handleSASLAuthdAuthentication service. +func (a *AuthState) handleSASLAuthdAuthentication(ctx *gin.Context) { switch a.handlePassword(ctx) { case global.AuthResultOK: a.authOK(ctx) @@ -216,8 +216,8 @@ func (a *AuthState) saslAuthd(ctx *gin.Context) { } } -// callback handles the execution of a Lua callback request in a Gin context. -func (a *AuthState) callback(ctx *gin.Context) { +// handleCallback handles the execution of a Lua handleCallback request in a Gin context. +func (a *AuthState) handleCallback(ctx *gin.Context) { if hookScript := config.LoadableConfig.GetLuaCallbackScriptPath(); hookScript != "" { hook.RunLuaCallback(ctx, hookScript) } @@ -351,8 +351,8 @@ func listBlockedAccounts(ctx context.Context, filterCmd *FilterCmd, guid string) return blockedAccounts, err } -// listBruteforce lists all blocked IP addresses and accounts in response to a brute force attack event. -func listBruteforce(ctx *gin.Context) { +// hanldeBruteForceList lists all blocked IP addresses and accounts in response to a brute force attack event. +func hanldeBruteForceList(ctx *gin.Context) { var filterCmd *FilterCmd guid := ctx.GetString(global.CtxGUIDKey) @@ -388,7 +388,7 @@ func listBruteforce(ctx *gin.Context) { }) } -// flushCache is a handler function for a Gin HTTP server. It takes a gin.Context as a parameter +// handleUserFlush is a handler function for a Gin HTTP server. It takes a gin.Context as a parameter // and attempts to flush the cache according to the *FlushUserCmd in the request's JSON body. // // Parameters: @@ -415,7 +415,7 @@ func listBruteforce(ctx *gin.Context) { // 5. If there are no binding errors, the function processes the cache flush. // 6. Based on the useCache flag and the outcome of the cache flush operation, the function // updates the statusMsg and sends the cache status to the client. -func flushCache(ctx *gin.Context) { +func handleUserFlush(ctx *gin.Context) { guid := ctx.GetString(global.CtxGUIDKey) userCmd := &FlushUserCmd{} @@ -676,13 +676,13 @@ func sendCacheStatus(ctx *gin.Context, guid string, userCmd *FlushUserCmd, useCa } } -// flushBruteForceRule handles the flushing of a brute force rule by processing the provided IP command and updating the necessary data. +// handleBruteForceRuleFlush handles the flushing of a brute force rule by processing the provided IP command and updating the necessary data. // It logs information about the action, including the GUID, brute force category, and flush operation. // If the IP command fails to bind, an error is logged, and a bad request status is returned. // If there is an error processing the brute force rules, an error is logged, and an internal server error status is returned. // If the rule flush error flag is true, the status message is set to "not flushed". // The function then logs the status message and returns a JSON response containing the GUID, brute force category, flush operation, and the result of the command, including the IP address -func flushBruteForceRule(ctx *gin.Context) { +func handleBruteForceRuleFlush(ctx *gin.Context) { var ( ruleFlushError bool removedKeys []string diff --git a/server/global/const.go b/server/global/const.go index fddf7efa..15529d00 100644 --- a/server/global/const.go +++ b/server/global/const.go @@ -479,14 +479,8 @@ const ImageCopyright = "Logo (c) by Roessner-Network-Solutions" // Categories and services. const ( - // CatMail is a constant for the "mail" category. - CatMail = "mail" - - // CatHTTP is a constant for the "http" category. - CatHTTP = "http" - - // CatGeneric is a constant for the "generic" category. - CatGeneric = "generic" + // CatAuth is a constant for the "generic" category. + CatAuth = "auth" // CatCache is a constant for the "cache" category. CatCache = "cache" @@ -500,21 +494,18 @@ const ( // ServSaslauthd is a constant for the "saslauthd" service. ServSaslauthd = "saslauthd" - // ServDovecot is a constant for the "dovecot" service. - ServDovecot = "dovecot" + // ServHeader is a constant for the "header" service. + ServHeader = "header" // ServCallback is a generic callback to call Lua ServCallback = "callback" - // ServBasicAuth is a constant for the "basicauth" service. - ServBasicAuth = "basicauth" + // ServBasic is a constant for the "basicauth" service. + ServBasic = "basic" // ServOryHydra is a constant for the "ory_hydra" service. ServOryHydra = "ory_hydra" - // ServUserInfo is a constant for the "user" service. - ServUserInfo = "user" - // ServJSON is a constant for the "json" service. ServJSON = "json"