Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 12 additions & 5 deletions providers/directory/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"encoding/base64"
"encoding/binary"
"fmt"
"github.com/brianvoe/gofakeit/v6"
log "github.com/sirupsen/logrus"
"math"
"mokapi/ldap"
"mokapi/runtime/events"
Expand All @@ -17,6 +15,9 @@ import (
"strings"
"time"
"unicode"

"github.com/brianvoe/gofakeit/v6"
log "github.com/sirupsen/logrus"
)

type predicate func(entry Entry) bool
Expand Down Expand Up @@ -375,7 +376,10 @@ func (p *parser) equal(name, value string) (predicate, error) {
return v == s
}
case "activeDirectoryObjectSidMatch", "0.0.0.0":
v, _ := sidToBytes(value)
v, err := sidToBytes(value)
if err != nil {
return nil, fmt.Errorf("invalid SID '%v': %v", value, err)
}
f = func(s string) bool {
b := []byte(s)
return bytes.Equal(b, v) || value == s
Expand Down Expand Up @@ -571,13 +575,16 @@ func sidToBytes(sid string) ([]byte, error) {
}
authId, authIdErr := strconv.ParseUint(parts[1], 10, 32)
if authIdErr != nil {
return nil, fmt.Errorf("invalid uint value %v at position: %v", parts[1], 1)
return nil, fmt.Errorf("invalid uint value '%v' at position: %v", parts[1], 1)
}
if authId > 255 {
return nil, fmt.Errorf("IdentifierAuthority value '%v' out of byte range (0-255) at position: %v", parts[1], 1)
}
result = append(result, byte(authId))
for i, part := range parts[2:] {
val, valErr := strconv.ParseUint(part, 10, 32)
if valErr != nil {
return nil, fmt.Errorf("invalid uint value %v at position: %v", part, i)
return nil, fmt.Errorf("invalid uint value '%v' at position: %v", part, i+2)
}
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, uint32(val))
Expand Down
127 changes: 109 additions & 18 deletions providers/directory/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/stretchr/testify/require"
"mokapi/config/dynamic"
"mokapi/config/dynamic/dynamictest"
"mokapi/engine/enginetest"
Expand All @@ -15,14 +14,17 @@ import (
"mokapi/try"
"strings"
"testing"

"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/require"
)

func TestSearch_Schema(t *testing.T) {
testcases := []struct {
name string
input string
reader dynamic.Reader
test func(t *testing.T, h ldap.Handler, err error)
test func(t *testing.T, h ldap.Handler, log *test.Hook, err error)
}{
{
name: "caseIgnoreMatch",
Expand All @@ -31,7 +33,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 2.5.4.3 NAME 'cn' DESC 'Common Name' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\ncn: UsEr")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -51,7 +53,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 1.3.6.1.1.1.1.0 NAME 'uidNumber' DESC 'User ID' \n EQUALITY integerMatch \n SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\nuidNumber: 1001")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -71,7 +73,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 1.3.6.1.4.1.99999.1.1 NAME 'customBinaryAttribute'\n DESC 'Example attribute storing raw binary data'\n EQUALITY octetStringMatch\n SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\ncustomBinaryAttribute:: bXlTZWNyZXREYXRh")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -91,7 +93,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 1.3.6.1.4.1.99999.1.1 NAME 'customBinaryAttribute'\n DESC 'Example attribute storing raw binary data'\n EQUALITY octetStringMatch\n SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\ncustomBinaryAttribute:: bXlTZWNyZXREYXRh")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -111,7 +113,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 1.3.6.1.4.1.99999.2.1 NAME 'isActive'\n DESC 'Indicates whether a user is active or not'\n EQUALITY booleanMatch\n SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\nisActive: TRUE")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -131,7 +133,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 1.3.6.1.4.1.99999.2.1 NAME 'isActive'\n DESC 'Indicates whether a user is active or not'\n EQUALITY booleanMatch\n SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\nisActive: TRUE")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -151,7 +153,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 1.3.6.1.4.1.99999.1.1\n NAME 'phoneNumber'\n DESC 'A phone number as a numeric string'\n EQUALITY numericStringMatch\n SYNTAX 1.3.6.1.4.1.1466.115.121.1.36\n SINGLE-VALUE )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\nphoneNumber: 00123456789")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -171,7 +173,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 1.3.6.1.4.1.99999.1.1\n NAME 'phoneNumber'\n DESC 'A phone number as a numeric string'\n EQUALITY numericStringMatch\n SYNTAX 1.3.6.1.4.1.1466.115.121.1.36\n SINGLE-VALUE )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\nphoneNumber: 00123456789")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -191,7 +193,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 1.3.6.1.4.1.99999.1.3\n NAME 'managerDN'\n DESC 'A manager distinguished name (DN)'\n EQUALITY distinguishedNameMatch\n SYNTAX 1.3.6.1.4.1.1466.115.121.1.12\n SINGLE-VALUE )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\nmanagerDN: cn=manager1,ou=employees,dc=example,dc=com")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -211,7 +213,7 @@ func TestSearch_Schema(t *testing.T) {
"file:/schema.ldif": {Raw: []byte("dn: \nsubschemaSubentry: cn=schema\n\ndn: cn=schema\nattributeTypes: ( 2.5.4.20\n NAME 'telephoneNumber'\n DESC 'Telephone number'\n EQUALITY telephoneNumberMatch \n SYNTAX 1.3.6.1.4.1.1466.115.121.1.50\n SINGLE-VALUE )")},
"file:/users.ldif": {Raw: []byte("dn: cn=user\ntelephoneNumber: +1 555 123 4567")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -230,7 +232,7 @@ func TestSearch_Schema(t *testing.T) {
reader: &dynamictest.Reader{Data: map[string]*dynamic.Config{
"file:/users.ldif": {Raw: []byte("dn: cn=user\nobjectSid:: AQUAAAAAAAUVAAAAF8sUcR3r8QcekDXQw9wAAA==")},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand Down Expand Up @@ -265,7 +267,7 @@ dn: cn=user2
objectSid:: AQUAAAAAAAUVAAAAF8sUcR3r8QcekDXQw9wAAA==
`)},
}},
test: func(t *testing.T, h ldap.Handler, err error) {
test: func(t *testing.T, h ldap.Handler, _ *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
Expand All @@ -279,21 +281,110 @@ objectSid:: AQUAAAAAAAUVAAAAF8sUcR3r8QcekDXQw9wAAA==
require.Equal(t, "cn=user1", res.Results[0].Dn)
},
},
{
name: "ldap filter objectSid using AD style with invalid authId",
input: `{ "files": [ "./users.ldif" ] }`,
reader: &dynamictest.Reader{Data: map[string]*dynamic.Config{
"file:/users.ldif": {Raw: []byte(`
dn:
namingContexts: dc=example_domain_name
subschemaSubentry: cn=schema

dn: cn=schema
objectClass: top
objectClass: subschema
attributeTypes: ( 1.2.3.4.5.6.7.8 NAME 'objectSid' DESC 'objectSid' EQUALITY activeDirectoryObjectSidMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
`)},
}},
test: func(t *testing.T, h ldap.Handler, log *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
h.ServeLDAP(rr, ldaptest.NewRequest(0, &ldap.SearchRequest{
Scope: ldap.ScopeWholeSubtree,
Filter: fmt.Sprintf("(objectSid=S-1-foo-21-1234567890-1234567890-1234567890-1001)"),
}))
res := rr.Message.(*ldap.SearchResponse)

require.Len(t, res.Results, 0)
require.Len(t, log.Entries, 2)
require.Equal(t, "ldap: filter syntax error: invalid SID 'S-1-foo-21-1234567890-1234567890-1234567890-1001': invalid uint value 'foo' at position: 1", log.Entries[1].Message)
},
},
{
name: "ldap filter objectSid using AD style with authId to high",
input: `{ "files": [ "./users.ldif" ] }`,
reader: &dynamictest.Reader{Data: map[string]*dynamic.Config{
"file:/users.ldif": {Raw: []byte(`
dn:
namingContexts: dc=example_domain_name
subschemaSubentry: cn=schema

dn: cn=schema
objectClass: top
objectClass: subschema
attributeTypes: ( 1.2.3.4.5.6.7.8 NAME 'objectSid' DESC 'objectSid' EQUALITY activeDirectoryObjectSidMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
`)},
}},
test: func(t *testing.T, h ldap.Handler, log *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
h.ServeLDAP(rr, ldaptest.NewRequest(0, &ldap.SearchRequest{
Scope: ldap.ScopeWholeSubtree,
Filter: fmt.Sprintf("(objectSid=S-1-300-21-1234567890-1234567890-1234567890-1001)"),
}))
res := rr.Message.(*ldap.SearchResponse)

require.Len(t, res.Results, 0)
require.Len(t, log.Entries, 2)
require.Equal(t, "ldap: filter syntax error: invalid SID 'S-1-300-21-1234567890-1234567890-1234567890-1001': IdentifierAuthority value '300' out of byte range (0-255) at position: 1", log.Entries[1].Message)
},
},
{
name: "ldap filter objectSid using AD style wrong format",
input: `{ "files": [ "./users.ldif" ] }`,
reader: &dynamictest.Reader{Data: map[string]*dynamic.Config{
"file:/users.ldif": {Raw: []byte(`
dn:
namingContexts: dc=example_domain_name
subschemaSubentry: cn=schema

dn: cn=schema
objectClass: top
objectClass: subschema
attributeTypes: ( 1.2.3.4.5.6.7.8 NAME 'objectSid' DESC 'objectSid' EQUALITY activeDirectoryObjectSidMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
`)},
}},
test: func(t *testing.T, h ldap.Handler, log *test.Hook, err error) {
require.NoError(t, err)

rr := ldaptest.NewRecorder()
h.ServeLDAP(rr, ldaptest.NewRequest(0, &ldap.SearchRequest{
Scope: ldap.ScopeWholeSubtree,
Filter: fmt.Sprintf("(objectSid=S-1-5-21-foo-1234567890-1234567890-1001)"),
}))
res := rr.Message.(*ldap.SearchResponse)

require.Len(t, res.Results, 0)
require.Len(t, log.Entries, 2)
require.Equal(t, "ldap: filter syntax error: invalid SID 'S-1-5-21-foo-1234567890-1234567890-1001': invalid uint value 'foo' at position: 3", log.Entries[1].Message)
},
},
}

t.Parallel()
for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
hook := test.NewGlobal()

var c *directory.Config
err := json.Unmarshal([]byte(tc.input), &c)
if err != nil {
tc.test(t, nil, err)
tc.test(t, nil, hook, err)
} else {
err = c.Parse(&dynamic.Config{Data: c, Info: dynamic.ConfigInfo{Url: try.MustUrl("file:/foo.yml")}}, tc.reader)
tc.test(t, directory.NewHandler(c, enginetest.NewEngine(), &eventstest.Handler{}), err)
tc.test(t, directory.NewHandler(c, enginetest.NewEngine(), &eventstest.Handler{}), hook, err)
}
})
}
Expand Down
Loading