diff --git a/auth/azure-ad.go b/auth/azure-ad.go index 76b861167..f5bdf4c5b 100644 --- a/auth/azure-ad.go +++ b/auth/azure-ad.go @@ -66,6 +66,15 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) { return } } + user, err := logic.GetUser(content.Email) + if err != nil { + handleOauthUserNotFound(w) + return + } + if !(user.IsSuperAdmin || user.IsAdmin) { + handleOauthUserNotAllowed(w) + return + } var newPass, fetchErr = fetchPassValue("") if fetchErr != nil { return diff --git a/auth/error.go b/auth/error.go index 002eed058..b982bc980 100644 --- a/auth/error.go +++ b/auth/error.go @@ -10,6 +10,31 @@ const oauthNotConfigured = `<!DOCTYPE html><html> </body> </html>` +const userNotAllowed = `<!DOCTYPE html><html> +<body> +<h3>Only Admins are allowed to access Dashboard.</h3> +<p>Non-Admins can access the netmaker networks using <a href="https://docs.netmaker.io/pro/rac.html" target="_blank" rel="noopener">RemoteAccessClient.</a></p> +</body> +</html> +` +const userNotFound = `<!DOCTYPE html><html> +<body> +<h3>User Not Found.</h3> +</body> +</html>` + +func handleOauthUserNotFound(response http.ResponseWriter) { + response.Header().Set("Content-Type", "text/html; charset=utf-8") + response.WriteHeader(http.StatusNotFound) + response.Write([]byte(userNotFound)) +} + +func handleOauthUserNotAllowed(response http.ResponseWriter) { + response.Header().Set("Content-Type", "text/html; charset=utf-8") + response.WriteHeader(http.StatusForbidden) + response.Write([]byte(userNotAllowed)) +} + // handleOauthNotConfigured - returns an appropriate html page when oauth is not configured on netmaker server but an oauth login was attempted func handleOauthNotConfigured(response http.ResponseWriter) { response.Header().Set("Content-Type", "text/html; charset=utf-8") diff --git a/auth/github.go b/auth/github.go index bdbbabc80..37c66085a 100644 --- a/auth/github.go +++ b/auth/github.go @@ -66,6 +66,15 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) { return } } + user, err := logic.GetUser(content.Email) + if err != nil { + handleOauthUserNotFound(w) + return + } + if !(user.IsSuperAdmin || user.IsAdmin) { + handleOauthUserNotAllowed(w) + return + } var newPass, fetchErr = fetchPassValue("") if fetchErr != nil { return diff --git a/auth/google.go b/auth/google.go index de144a3cc..e61ab4c7c 100644 --- a/auth/google.go +++ b/auth/google.go @@ -68,6 +68,15 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) { return } } + user, err := logic.GetUser(content.Email) + if err != nil { + handleOauthUserNotFound(w) + return + } + if !(user.IsSuperAdmin || user.IsAdmin) { + handleOauthUserNotAllowed(w) + return + } var newPass, fetchErr = fetchPassValue("") if fetchErr != nil { return diff --git a/auth/oidc.go b/auth/oidc.go index 86530bc91..d38ddeea0 100644 --- a/auth/oidc.go +++ b/auth/oidc.go @@ -79,6 +79,15 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) { return } } + user, err := logic.GetUser(content.Email) + if err != nil { + handleOauthUserNotFound(w) + return + } + if !(user.IsSuperAdmin || user.IsAdmin) { + handleOauthUserNotAllowed(w) + return + } var newPass, fetchErr = fetchPassValue("") if fetchErr != nil { return diff --git a/controllers/controller.go b/controllers/controller.go index 8e54d38b1..d80093beb 100644 --- a/controllers/controller.go +++ b/controllers/controller.go @@ -41,7 +41,7 @@ func HandleRESTRequests(wg *sync.WaitGroup, ctx context.Context) { // Currently allowed dev origin is all. Should change in prod // should consider analyzing the allowed methods further - headersOk := handlers.AllowedHeaders([]string{"Access-Control-Allow-Origin", "X-Requested-With", "Content-Type", "authorization"}) + headersOk := handlers.AllowedHeaders([]string{"Access-Control-Allow-Origin", "X-Requested-With", "Content-Type", "authorization", "From-Ui"}) originsOk := handlers.AllowedOrigins(strings.Split(servercfg.GetAllowedOrigin(), ",")) methodsOk := handlers.AllowedMethods([]string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete}) diff --git a/controllers/user.go b/controllers/user.go index 64dd187a2..76e712182 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -71,6 +71,20 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) { logic.ReturnErrorResponse(response, request, errorResponse) return } + if val := request.Header.Get("From-Ui"); val == "true" { + // request came from UI, if normal user block Login + user, err := logic.GetUser(authRequest.UserName) + if err != nil { + logger.Log(0, authRequest.UserName, "user validation failed: ", + err.Error()) + logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized")) + return + } + if !(user.IsAdmin || user.IsSuperAdmin) { + logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("only admins can access dashboard"), "unauthorized")) + return + } + } username := authRequest.UserName jwt, err := logic.VerifyAuthRequest(authRequest) if err != nil { @@ -119,7 +133,7 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) { if client.OwnerID == username && !client.Enabled { slog.Info(fmt.Sprintf("enabling ext client %s for user %s due to RAC autodisabling feature", client.ClientID, client.OwnerID)) if newClient, err := logic.ToggleExtClientConnectivity(&client, true); err != nil { - slog.Error("error disabling ext client in RAC autodisable hook", "error", err) + slog.Error("error enabling ext client in RAC autodisable hook", "error", err) continue // dont return but try for other clients } else { // publish peer update to ingress gateway