diff --git a/litmus-portal/graphql-server/pkg/authorization/middleware.go b/litmus-portal/graphql-server/pkg/authorization/middleware.go index 02b99d7fd..d40c7c4c8 100644 --- a/litmus-portal/graphql-server/pkg/authorization/middleware.go +++ b/litmus-portal/graphql-server/pkg/authorization/middleware.go @@ -2,8 +2,9 @@ package authorization import ( "context" - "fmt" "net/http" + + "github.com/sirupsen/logrus" ) type contextKey string @@ -30,8 +31,8 @@ func Middleware(handler http.Handler) http.Handler { }) } -// RestMiddleware ... -func RestMiddleware(handler http.Handler) http.Handler { +// RestMiddlewareWithRole verifies jwt and checks if user has enough privilege to access route +func RestMiddlewareWithRole(handler http.Handler, roles []string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { jwt := "" auth, err := r.Cookie(CookieName) @@ -40,14 +41,24 @@ func RestMiddleware(handler http.Handler) http.Handler { } else if r.Header.Get("Authorization") != "" { jwt = r.Header.Get("Authorization") } - _, err = UserValidateJWT(jwt) + user, err := UserValidateJWT(jwt) if err != nil { - fmt.Println("Error in Cookie: ", err) + logrus.WithError(err).Error("Invalid Auth Cookie") w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("Error verifying JWT token: " + err.Error())) return } - - handler.ServeHTTP(w, r) + if len(roles) == 0 { + handler.ServeHTTP(w, r) + return + } + for _, role := range roles { + if role == user["role"] { + handler.ServeHTTP(w, r) + return + } + } + w.WriteHeader(http.StatusUnauthorized) + return }) } diff --git a/litmus-portal/graphql-server/pkg/database/mongodb/cluster/operations.go b/litmus-portal/graphql-server/pkg/database/mongodb/cluster/operations.go index a8df78cfd..425a51ce1 100644 --- a/litmus-portal/graphql-server/pkg/database/mongodb/cluster/operations.go +++ b/litmus-portal/graphql-server/pkg/database/mongodb/cluster/operations.go @@ -82,3 +82,17 @@ func GetClusterWithProjectID(projectID string, clusterType *string) ([]*Cluster, return clusters, nil } + +// GetClusters returns all the clusters matching the query +func GetClusters(ctx context.Context, query bson.D) ([]*Cluster, error) { + var clusters []*Cluster + results, err := mongodb.Operator.List(ctx, mongodb.ClusterCollection, query) + if err != nil { + return []*Cluster{}, err + } + err = results.All(ctx, &clusters) + if err != nil { + return []*Cluster{}, err + } + return clusters, nil +} diff --git a/litmus-portal/graphql-server/pkg/database/mongodb/project/operations.go b/litmus-portal/graphql-server/pkg/database/mongodb/project/operations.go index 3e47eca84..767c251db 100644 --- a/litmus-portal/graphql-server/pkg/database/mongodb/project/operations.go +++ b/litmus-portal/graphql-server/pkg/database/mongodb/project/operations.go @@ -41,6 +41,22 @@ func GetProject(ctx context.Context, query bson.D) (*Project, error) { return project, err } +// GetProjects takes a query parameter to retrieve the projects that match query +func GetProjects(ctx context.Context, query bson.D) ([]Project, error) { + results, err := mongodb.Operator.List(ctx, mongodb.ProjectCollection, query) + if err != nil { + return nil, err + } + + var projects []Project + err = results.All(ctx, &projects) + if err != nil { + return nil, err + } + + return projects, nil +} + // GetProjectsByUserID returns a project based on the userID func GetProjectsByUserID(ctx context.Context, userID string) ([]Project, error) { var projects []Project diff --git a/litmus-portal/graphql-server/pkg/database/mongodb/usermanagement/operations.go b/litmus-portal/graphql-server/pkg/database/mongodb/usermanagement/operations.go index 2d647bb49..46c55c411 100644 --- a/litmus-portal/graphql-server/pkg/database/mongodb/usermanagement/operations.go +++ b/litmus-portal/graphql-server/pkg/database/mongodb/usermanagement/operations.go @@ -59,8 +59,7 @@ func GetUserByUserID(ctx context.Context, userID string) (*User, error) { } // GetUsers returns the list of users present in the project -func GetUsers(ctx context.Context) ([]User, error) { - query := bson.D{{}} +func GetUsers(ctx context.Context, query bson.D) ([]User, error) { result, err := mongodb.Operator.List(ctx, mongodb.UserCollection, query) if err != nil { log.Print("Error getting users : ", err) diff --git a/litmus-portal/graphql-server/pkg/stat/status.go b/litmus-portal/graphql-server/pkg/stat/status.go new file mode 100644 index 000000000..06a8fdb77 --- /dev/null +++ b/litmus-portal/graphql-server/pkg/stat/status.go @@ -0,0 +1,85 @@ +package stat + +import ( + "encoding/json" + "net/http" + + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/cluster" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/project" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/usermanagement" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/utils" + "github.com/sirupsen/logrus" + "go.mongodb.org/mongo-driver/bson" +) + +type AgentCount struct { + Namespaced int `json:"ns_scope"` + Cluster int `json:"cluster_scope"` + Total int `json:"total_count"` +} +type OverviewStat struct { + UserCount int `json:"user_count"` + ProjectCount int `json:"project_count"` + AgentCount AgentCount `json:"agent_count"` + WorkflowScheduleCount int `json:"schedule_count"` + WorkflowRunCount int `json:"run_count"` +} + +//GetStats returns the portals overview +func GetStats(w http.ResponseWriter, r *http.Request) { + users, err := usermanagement.GetUsers(r.Context(), bson.D{}) + if err != nil { + logrus.WithError(err).Errorf("failed to get users for stat") + utils.WriteHeaders(&w, http.StatusInternalServerError) + return + } + agentNS, err := cluster.GetClusters(r.Context(), bson.D{{"agent_scope", "namespace"}, {"is_removed", false}}) + if err != nil { + logrus.WithError(err).Errorf("failed to get agents(ns) for stat") + utils.WriteHeaders(&w, http.StatusInternalServerError) + return + } + agents, err := cluster.GetClusters(r.Context(), bson.D{{"is_removed", false}}) + if err != nil { + logrus.WithError(err).Errorf("failed to get agents for stat") + utils.WriteHeaders(&w, http.StatusInternalServerError) + return + } + projects, err := project.GetProjects(r.Context(), bson.D{}) + if err != nil { + logrus.WithError(err).Errorf("failed to get projects for stat") + utils.WriteHeaders(&w, http.StatusInternalServerError) + return + } + workflows, err := workflow.GetWorkflows(bson.D{}) + if err != nil { + logrus.WithError(err).Errorf("failed to get workflows for stat") + utils.WriteHeaders(&w, http.StatusInternalServerError) + return + } + runCount := 0 + for _, workFlowInput := range workflows { + runCount += len(workFlowInput.WorkflowRuns) + } + + overview := OverviewStat{ + UserCount: len(users), + ProjectCount: len(projects), + WorkflowRunCount: runCount, + WorkflowScheduleCount: len(workflows), + AgentCount: AgentCount{ + Namespaced: len(agentNS), + Total: len(agents), + Cluster: len(agents) - len(agentNS), + }, + } + data, err := json.Marshal(overview) + if err != nil { + logrus.WithError(err).Errorf("failed to marshal stat") + utils.WriteHeaders(&w, http.StatusInternalServerError) + return + } + utils.WriteHeaders(&w, http.StatusOK) + w.Write(data) +} diff --git a/litmus-portal/graphql-server/pkg/usermanagement/create.go b/litmus-portal/graphql-server/pkg/usermanagement/create.go index eb97920f5..d73ab2c29 100644 --- a/litmus-portal/graphql-server/pkg/usermanagement/create.go +++ b/litmus-portal/graphql-server/pkg/usermanagement/create.go @@ -6,12 +6,12 @@ import ( "log" "time" - "go.mongodb.org/mongo-driver/mongo" - "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model" dbOperationsUserManagement "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/usermanagement" dbSchemaUserManagement "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/usermanagement" "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/project" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" ) const ( @@ -73,7 +73,7 @@ func GetUser(ctx context.Context, username string) (*model.User, error) { // GetUsers queries the list of all the users from the DB and returns it in the appropriate format func GetUsers(ctx context.Context) ([]*model.User, error) { - users, err := dbOperationsUserManagement.GetUsers(ctx) + users, err := dbOperationsUserManagement.GetUsers(ctx, bson.D{}) if err != nil { return nil, err } diff --git a/litmus-portal/graphql-server/server.go b/litmus-portal/graphql-server/server.go index 7e295ac57..7c8bf95cb 100644 --- a/litmus-portal/graphql-server/server.go +++ b/litmus-portal/graphql-server/server.go @@ -7,22 +7,21 @@ import ( "runtime" "time" - "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb" - "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler/extension" "github.com/99designs/gqlgen/graphql/handler/transport" "github.com/99designs/gqlgen/graphql/playground" "github.com/gorilla/mux" "github.com/gorilla/websocket" - "github.com/rs/cors" - "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph" "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/generated" "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/authorization" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb" "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/file_handlers" gitOpsHandler "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/gitops/handler" "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/myhub" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/stat" + "github.com/rs/cors" ) const defaultPort = "8080" @@ -75,7 +74,8 @@ func main() { router.Handle("/", playground.Handler("GraphQL playground", "/query")) router.Handle("/query", authorization.Middleware(srv)) router.HandleFunc("/file/{key}{path:.yaml}", file_handlers.FileHandler) - router.Handle("/icon/{ProjectID}/{HubName}/{ChartName}/{IconName}", authorization.RestMiddleware(myhub.GetIconHandler)).Methods("GET") + router.Handle("/icon/{ProjectID}/{HubName}/{ChartName}/{IconName}", authorization.RestMiddlewareWithRole(myhub.GetIconHandler, nil)).Methods("GET") + router.Handle("/stats", authorization.RestMiddlewareWithRole(http.HandlerFunc(stat.GetStats), []string{"admin"})).Methods("GET") log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) log.Fatal(http.ListenAndServe(":"+port, router))