package view import ( "context" "encoding/json" "fmt" "slices" "github.com/shurcooL/graphql" "edge-infra.dev/pkg/edge/api/client" "edge-infra.dev/pkg/edge/api/graph/model" "edge-infra.dev/pkg/edge/edgeadmin/commands/operatorintervention/edgeextension" "edge-infra.dev/pkg/edge/edgecli" "edge-infra.dev/pkg/edge/edgecli/flagutil" "edge-infra.dev/pkg/lib/cli/command" "edge-infra.dev/pkg/lib/cli/rags" ) const ( longHelpStr = ` This command returns a readable JSON-formatted summary of all role mappings and their allowed commands from the database. $ edgeadmin operatorintervention view summary By default this command will return a summary of all roles on the database, but you can also specify just a subset of roles using any number of optional --role flags. $ edgeadmin operatorintervention view summary --role role1 --role role2 --role role3 ` ) type RoleConfiguration struct { Role string Privileges []model.Rule } func NewSummary(cfg *edgecli.Config) *command.Command { var ( edge = &edgeextension.Ext{Cfg: cfg} roles []string ) return &command.Command{ ShortUsage: "edgeadmin operatorintervention view summary [--role ]", ShortHelp: "view the summary of the full operator intervention rules configuration", LongHelp: longHelpStr, Flags: []*rags.Rag{ { Name: flagutil.RoleFlag, Usage: "(optional) filter results by edge role name", Value: &rags.StringSet{ Var: &roles, }, }, }, Extensions: []command.Extension{ edge, }, Exec: func(ctx context.Context, _ []string) error { roleMappings, rules, err := retrieveRolesAndRules(ctx, edge.Client, roles) if err != nil { return err } roleConfigs := assembleRoleConfigurations(roleMappings, rules) output, err := json.MarshalIndent(roleConfigs, "", " ") if err != nil { return err } fmt.Println(string(output)) return nil }, } } // Query graphql for all OIRoleMappings and OIRules. // Takes in optional list of roles as a filter for OIRoleMappings. func retrieveRolesAndRules(ctx context.Context, bffClient *client.EdgeClient, roles []string) ([]model.OiRoleMapping, []model.Rule, error) { var query struct { OiRoleMappings []model.OiRoleMapping `graphql:"operatorInterventionRoleMappings(roles: $roles)"` Rules []model.Rule `graphql:"operatorInterventionRules"` } graphqlRoles := []graphql.String{} for _, role := range roles { graphqlRoles = append(graphqlRoles, graphql.String(role)) } variables := map[string]any{ "roles": graphqlRoles, } err := bffClient.Query(ctx, &query, variables) if err != nil { return nil, nil, fmt.Errorf("error calling Edge API: %w", err) } return query.OiRoleMappings, query.Rules, nil } // Add all rule configurations for each role func assembleRoleConfigurations(roleMappings []model.OiRoleMapping, rules []model.Rule) []RoleConfiguration { var roleConfigs []RoleConfiguration for _, roleMapping := range roleMappings { // Add all rules matching a role's privileges roleConfig := assembleRulesForRole(roleMapping, rules) roleConfigs = append(roleConfigs, roleConfig) } return roleConfigs } // Add all rules matching a role's privileges func assembleRulesForRole(roleMapping model.OiRoleMapping, rules []model.Rule) RoleConfiguration { roleConfig := RoleConfiguration{Role: roleMapping.Role.String()} for _, priv := range roleMapping.Privileges { rule := model.Rule{Privilege: priv} // If there are any rules for a role's privilege, add that rule to roleConfig if slices.ContainsFunc(rules, func(r model.Rule) bool { rule.Commands = r.Commands return *r.Privilege == *priv }) { roleConfig.Privileges = append(roleConfig.Privileges, rule) } } return roleConfig }