Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support to new crossprofiles #6

Merged
merged 5 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 6 additions & 18 deletions config/sso_extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"os"
"slices"
"strings"

"github.com/argoproj/argo-workflows/v3/server/auth/devhub"
Expand All @@ -13,6 +12,7 @@ import (
type SSOExtendedLabel struct {
ApiPassword string `json:"apiPassword,omitempty"`
ApiUrl string `json:"apiUrl,omitempty"`
ApiEndpoint string `json:"apiEndpoint,omitempty"`
Label string `json:"label,omitempty"`
WriteGroups []string `json:"writeGroups,omitempty"`
// The AdminGroup does not filter by label gets all the objects
Expand All @@ -29,30 +29,18 @@ func CanDelegateByLabel() bool {
return os.Getenv("SSO_DELEGATE_RBAC_TO_LABEL") == "true"
}

func RbacDelegateToLabel(ctx context.Context, mail string, apiUrl, apiPassword, label string, writeGroups []string) (*ResourcesToFilter, error) {
func RbacDelegateToLabel(ctx context.Context, mail string, apiUrl, apiEndpoint, apiPassword, label string, writeGroups []string) (*ResourcesToFilter, error) {
resourcesToFilterPopulated := &ResourcesToFilter{}
devhubClient := devhub.NewClient()
mailParts := strings.Split(mail, "@")
servicesAndRoles, err := devhub.GetServicesAndRoles(devhubClient, apiUrl, apiPassword, mailParts[0])
servicesAndGroup, err := devhub.GetServicesAndGroup(devhubClient, apiUrl, apiEndpoint, apiPassword, mailParts[0], writeGroups)
if err != nil {
fmt.Printf("Can't Procces the petition on devhub to get roles %+v", err)
}
resourcesToFilterPopulated.Group = getUserGroup(servicesAndRoles.Roles, writeGroups)
if servicesAndRoles.Services != nil {
for service := range servicesAndRoles.Services {
resourcesToFilterPopulated.ArrayLabels = append(resourcesToFilterPopulated.ArrayLabels, service)
}
resourcesToFilterPopulated.Group = servicesAndGroup.Group
if servicesAndGroup.Services != nil {
resourcesToFilterPopulated.ArrayLabels = append(resourcesToFilterPopulated.ArrayLabels, servicesAndGroup.Services...)
resourcesToFilterPopulated.LabelsFilter = fmt.Sprintf("%s in (%s)", label, strings.Join(resourcesToFilterPopulated.ArrayLabels[:], ","))
}
return resourcesToFilterPopulated, nil
}

func getUserGroup(roles map[string]string, writeGroups []string) string {
sabyRole := "reader"
for role := range roles {
if slices.Contains(writeGroups, role) {
sabyRole = "writer"
}
}
return sabyRole
}
79 changes: 57 additions & 22 deletions server/auth/devhub/aplication.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,76 @@ package devhub
import (
"encoding/json"
"fmt"
"slices"
)

type RolesAndServices struct {
Roles map[string]string
Services map[string]string
type GroupAndServices struct {
Services []string
Group string
}

func GetServicesAndRoles(devhubclient *Client, apiUrl, apiPassword, userToIdentify string) (*RolesAndServices, error) {
roles := make(map[string]string)
services := make(map[string]string)
servicesAndRoles := &RolesAndServices{}
apiDevhub := fmt.Sprintf("%s/api/identity/%s", apiUrl, userToIdentify)
func GetServicesAndGroup(devhubclient *Client, apiUrl, apiEndpoint, apiPassword, userToIdentify string, writeGroups []string) (*GroupAndServices, error) {
var result map[string]interface{}
Copy link

@DanielDorado DanielDorado Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, it is better to pass a struct because the response is typed, then we do not have to cast and check if it is a string or not in GetRolesAndServices.

I have not tested it, but getting the OpenAPI specification and using https://github.com/OpenAPITools/openapi-generator will create a good structure.

var roles []string
var services []string
servicesAndGroup := &GroupAndServices{}
apiDevhub := fmt.Sprintf("%s/%s/%s", apiUrl, apiEndpoint, userToIdentify)
res, err := HandleRequestApiInditex(devhubclient, apiDevhub, "GET", apiPassword, map[string]interface{}{})
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
return nil, err
}
if teams, ok := result["teams"].([]interface{}); ok {
for _, team := range teams {
if len(team.(map[string]interface{})["projects"].([]interface{})) > 0 {
for _, project := range team.(map[string]interface{})["projects"].([]interface{}) {
if relationshipType, ok := project.(map[string]interface{})["relationshipType"].(map[string]interface{}); ok && relationshipType["name"] == "Owner" {
services[project.(map[string]interface{})["key"].(string)] = "service"
for _, profile := range team.(map[string]interface{})["profiles"].([]interface{}) {
roles[profile.(map[string]interface{})["name"].(string)] = "role"
}
}

roles, services = GetRolesAndServices(result, services, roles)

servicesAndGroup.Group = GetGroupByRole(writeGroups, roles)
servicesAndGroup.Services = services
return servicesAndGroup, nil
}

func GetRolesAndServices(result map[string]interface{}, services, roles []string) ([]string, []string) {
teams, ok := result["teams"].([]interface{})
if !ok {
return services, roles
}
for _, team := range teams {
if len(team.(map[string]interface{})["applications"].([]interface{})) <= 0 {
continue
}
for _, project := range team.(map[string]interface{})["applications"].([]interface{}) {
if project.(map[string]interface{})["relationshipType"].(string) != "Owner" {
continue
}
if !slices.Contains(services, project.(map[string]interface{})["key"].(string)) {
services = append(services, project.(map[string]interface{})["key"].(string))
}
for _, profile := range team.(map[string]interface{})["profiles"].([]interface{}) {
if !slices.Contains(roles, profile.(map[string]interface{})["name"].(string)) {
roles = append(roles, profile.(map[string]interface{})["name"].(string))
}
}
crossprofiles, ok := result["crossProfiles"].([]interface{})
if !ok {
continue
}
for _, crossprofile := range crossprofiles {
if !slices.Contains(roles, crossprofile.(map[string]interface{})["name"].(string)) {
roles = append(roles, crossprofile.(map[string]interface{})["name"].(string))
}
}
}
}
servicesAndRoles.Roles = roles
servicesAndRoles.Services = services
return servicesAndRoles, nil
return roles, services
}

func GetGroupByRole(writeGroups []string, roles []string) string {
groupByRole := "reader"
for _, role := range roles {
if slices.Contains(writeGroups, role) {
groupByRole = "writer"
}
}
return groupByRole
}
3 changes: 2 additions & 1 deletion server/auth/sso/sso.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ func newSso(
lf["issuerAlias"] = c.IssuerAlias
}
ssoExtendedConfigurate.ApiUrl = c.SSOExtendedLabel.ApiUrl
ssoExtendedConfigurate.ApiEndpoint = c.SSOExtendedLabel.ApiEndpoint
ssoExtendedConfigurate.ApiPassword = c.SSOExtendedLabel.ApiPassword
ssoExtendedConfigurate.Label = c.SSOExtendedLabel.Label
ssoExtendedConfigurate.AdminGroup = c.SSOExtendedLabel.AdminGroup
Expand Down Expand Up @@ -331,7 +332,7 @@ func (s *sso) HandleCallback(w http.ResponseWriter, r *http.Request) {
}
}
if !c.TeamFilterClaims.IsAdmin {
resourcesFilter, err := config.RbacDelegateToLabel(ctx, c.Email, ssoExtendedLabelConfig.ApiUrl, ssoExtendedLabelConfig.ApiPassword, ssoExtendedLabelConfig.Label, ssoExtendedLabelConfig.WriteGroups)
resourcesFilter, err := config.RbacDelegateToLabel(ctx, c.Email, ssoExtendedLabelConfig.ApiUrl, ssoExtendedLabelConfig.ApiEndpoint, ssoExtendedLabelConfig.ApiPassword, ssoExtendedLabelConfig.Label, ssoExtendedLabelConfig.WriteGroups)
if err != nil {
log.WithError(err).Error("failed to perform RBAC authorization")
}
Expand Down
2 changes: 2 additions & 0 deletions server/auth/sso/sso_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ func TestNewSsoWithExtendedSSO(t *testing.T) {
SSOExtendedLabel: config.SSOExtendedLabel{
ApiPassword: "testPassword",
ApiUrl: "testApiUrl",
ApiEndpoint: "testApiEndpoint",
AdminGroup: "testAdminGroup",
Label: "testLabel",
WriteGroups: []string{"testWriteGroups"},
Expand All @@ -172,6 +173,7 @@ func TestNewSsoWithExtendedSSO(t *testing.T) {
assert.Equal(t, "testAdminGroup", ssoObject.SSOExtendedLabel.AdminGroup)
assert.Equal(t, "testPassword", ssoObject.SSOExtendedLabel.ApiPassword)
assert.Equal(t, "testApiUrl", ssoObject.SSOExtendedLabel.ApiUrl)
assert.Equal(t, "testApiEndpoint", ssoObject.SSOExtendedLabel.ApiEndpoint)
assert.Equal(t, "testLabel", ssoObject.SSOExtendedLabel.Label)
assert.Equal(t, 1, len(ssoObject.SSOExtendedLabel.WriteGroups))
}
Expand Down