Auth: Add support for escaping colon characters in org_mapping (#89951)

* Split org_mapping correctly if it contains multiple colons

* Improve tests

* Use backslash as an escape character for colons

* Cleanup, address feedback

* Change test to use double quotes as an example
This commit is contained in:
Misi 2024-07-05 12:06:25 +02:00 committed by GitHub
parent 7a78ad3893
commit 4f06568f8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 59 additions and 7 deletions

View File

@ -3,6 +3,7 @@ package connectors
import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
@ -11,7 +12,12 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
const mapperMatchAllOrgID = -1
const (
mapperMatchAllOrgID = -1
escapeStr = `\`
)
var separatorRegexp = regexp.MustCompile(":")
// OrgRoleMapper maps external orgs/groups to Grafana orgs and basic roles.
type OrgRoleMapper struct {
@ -132,7 +138,7 @@ func (m *OrgRoleMapper) ParseOrgMappingSettings(ctx context.Context, mappings []
res := map[string]map[int64]org.RoleType{}
for _, v := range mappings {
kv := strings.Split(v, ":")
kv := splitOrgMapping(v)
if !isValidOrgMappingFormat(kv) {
m.logger.Error("Skipping org mapping due to invalid format.", "mapping", fmt.Sprintf("%v", v))
if roleStrict {
@ -203,6 +209,29 @@ func (m *OrgRoleMapper) getAllOrgs() (map[int64]bool, error) {
return allOrgIDs, nil
}
func splitOrgMapping(mapping string) []string {
result := make([]string, 0, 3)
matches := separatorRegexp.FindAllStringIndex(mapping, -1)
from := 0
for _, match := range matches {
// match[0] is the start, match[1] is the end of the match
start, end := match[0], match[1]
// Check if the match is not preceded by two backslashes
if start == 0 || mapping[start-1:start] != escapeStr {
result = append(result, strings.ReplaceAll(mapping[from:end-1], escapeStr, ""))
from = end
}
}
result = append(result, mapping[from:])
if len(result) > 3 {
return []string{}
}
return result
}
func getRoleForInternalOrgMapping(kv []string) org.RoleType {
if len(kv) > 2 && org.RoleType(kv[2]).IsValid() {
return org.RoleType(kv[2])

View File

@ -268,6 +268,29 @@ func TestOrgRoleMapper_ParseOrgMappingSettings(t *testing.T) {
strictRoleMapping: false,
},
},
{
name: "should return correct mapping when the first part contains multiple colons",
rawMapping: []string{"Groups\\:IT\\:ops:1:Viewer"},
roleStrict: false,
expected: &MappingConfiguration{
orgMapping: map[string]map[int64]org.RoleType{"Groups:IT:ops": {1: org.RoleViewer}},
strictRoleMapping: false,
},
},
{
name: "should return correct mapping when the org name contains multiple colons",
rawMapping: []string{`Group1:Org\:1:Viewer`},
roleStrict: false,
setupMock: func(orgService *orgtest.MockService) {
orgService.On("GetByName", mock.Anything, mock.MatchedBy(func(query *org.GetOrgByNameQuery) bool {
return query.Name == "Org:1"
})).Return(&org.Org{ID: 1}, nil)
},
expected: &MappingConfiguration{
orgMapping: map[string]map[int64]org.RoleType{"Group1": {1: org.RoleViewer}},
strictRoleMapping: false,
},
},
{
name: "should return empty mapping when org mapping is nil",
rawMapping: nil,
@ -277,8 +300,8 @@ func TestOrgRoleMapper_ParseOrgMappingSettings(t *testing.T) {
},
},
{
name: "should return empty mapping when the org mapping format is invalid and strict role mapping is enabled",
rawMapping: []string{"External:Org1:First:Organization:Editor"},
name: "should return empty mapping when one of the org mappings are not in the correct format and strict role mapping is enabled",
rawMapping: []string{"Second:Group:1:SuperEditor", "Second:1:Viewer"},
roleStrict: true,
expected: &MappingConfiguration{
orgMapping: map[string]map[int64]org.RoleType{},
@ -286,11 +309,11 @@ func TestOrgRoleMapper_ParseOrgMappingSettings(t *testing.T) {
},
},
{
name: "should return only the valid mappings from the raw mappings when strict role mapping is disabled",
rawMapping: []string{"External:Org1:First:Organization:Editor", "Second:1:Editor"},
name: "should skip org mapping when one of the org mappings are not in the correct format and strict role mapping is enabled",
rawMapping: []string{"Second:Group:1:SuperEditor", "Second:1:Admin"},
roleStrict: false,
expected: &MappingConfiguration{
orgMapping: map[string]map[int64]org.RoleType{"Second": {1: org.RoleEditor}},
orgMapping: map[string]map[int64]org.RoleType{"Second": {1: org.RoleAdmin}},
strictRoleMapping: false,
},
},