mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-06 04:09:40 -06:00
Previously we only allowed setting the "identities" property on education users. This changes move the related code to the main user object.
454 lines
14 KiB
Go
454 lines
14 KiB
Go
package identity
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/CiscoM31/godata"
|
|
"github.com/go-ldap/ldap/v3"
|
|
"github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
var groupEntry = ldap.NewEntry("cn=group",
|
|
map[string][]string{
|
|
"cn": {"group"},
|
|
"entryuuid": {"abcd-defg"},
|
|
"member": {
|
|
"uid=user,ou=people,dc=test",
|
|
"uid=invalid,ou=people,dc=test",
|
|
},
|
|
})
|
|
|
|
var invalidGroupEntry = ldap.NewEntry("cn=invalid",
|
|
map[string][]string{
|
|
"cn": {"invalid"},
|
|
})
|
|
|
|
var queryParamExpand = url.Values{
|
|
"$expand": []string{"members"},
|
|
}
|
|
|
|
var queryParamSelect = url.Values{
|
|
"$select": []string{"members"},
|
|
}
|
|
|
|
var groupLookupSearchRequest = &ldap.SearchRequest{
|
|
BaseDN: "ou=groups,dc=test",
|
|
Scope: 2,
|
|
SizeLimit: 1,
|
|
Filter: "(&(objectClass=groupOfNames)(|(cn=group)(entryUUID=group)))",
|
|
Attributes: []string{"cn", "entryUUID", "member"},
|
|
Controls: []ldap.Control(nil),
|
|
}
|
|
|
|
var groupListSearchRequest = &ldap.SearchRequest{
|
|
BaseDN: "ou=groups,dc=test",
|
|
Scope: 2,
|
|
Filter: "(&(objectClass=groupOfNames))",
|
|
Attributes: []string{"cn", "entryUUID", "member"},
|
|
Controls: []ldap.Control(nil),
|
|
}
|
|
|
|
func TestGetGroup(t *testing.T) {
|
|
// Mock a Sizelimit Error
|
|
lm := &mocks.Client{}
|
|
lm.On("Search", mock.Anything).Return(nil, ldap.NewError(ldap.LDAPResultSizeLimitExceeded, errors.New("mock")))
|
|
|
|
b, _ := getMockedBackend(lm, lconfig, &logger)
|
|
_, err := b.GetGroup(context.Background(), "group", nil)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
_, err = b.GetGroup(context.Background(), "group", queryParamExpand)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
_, err = b.GetGroup(context.Background(), "group", queryParamSelect)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
|
|
// Mock an empty Search Result
|
|
lm = &mocks.Client{}
|
|
lm.On("Search", mock.Anything).Return(&ldap.SearchResult{}, nil)
|
|
b, _ = getMockedBackend(lm, lconfig, &logger)
|
|
_, err = b.GetGroup(context.Background(), "group", nil)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
_, err = b.GetGroup(context.Background(), "group", queryParamExpand)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
_, err = b.GetGroup(context.Background(), "group", queryParamSelect)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
|
|
// Mock an invalid Search Result
|
|
lm = &mocks.Client{}
|
|
lm.On("Search", mock.Anything).Return(&ldap.SearchResult{
|
|
Entries: []*ldap.Entry{invalidGroupEntry},
|
|
}, nil)
|
|
b, _ = getMockedBackend(lm, lconfig, &logger)
|
|
_, err = b.GetGroup(context.Background(), "group", nil)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
_, err = b.GetGroup(context.Background(), "group", queryParamExpand)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
_, err = b.GetGroup(context.Background(), "group", queryParamSelect)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
|
|
// Mock a valid Search Result
|
|
lm = &mocks.Client{}
|
|
sr2 := &ldap.SearchRequest{
|
|
BaseDN: "uid=user,ou=people,dc=test",
|
|
SizeLimit: 1,
|
|
Filter: "(objectClass=inetOrgPerson)",
|
|
Attributes: ldapUserAttributes,
|
|
Controls: []ldap.Control(nil),
|
|
}
|
|
sr3 := &ldap.SearchRequest{
|
|
BaseDN: "uid=invalid,ou=people,dc=test",
|
|
SizeLimit: 1,
|
|
Filter: "(objectClass=inetOrgPerson)",
|
|
Attributes: ldapUserAttributes,
|
|
Controls: []ldap.Control(nil),
|
|
}
|
|
|
|
lm.On("Search", groupLookupSearchRequest).Return(&ldap.SearchResult{Entries: []*ldap.Entry{groupEntry}}, nil)
|
|
lm.On("Search", sr2).Return(&ldap.SearchResult{Entries: []*ldap.Entry{userEntry}}, nil)
|
|
lm.On("Search", sr3).Return(&ldap.SearchResult{Entries: []*ldap.Entry{invalidUserEntry}}, nil)
|
|
b, _ = getMockedBackend(lm, lconfig, &logger)
|
|
g, err := b.GetGroup(context.Background(), "group", nil)
|
|
if err != nil {
|
|
t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
|
|
} else if *g.Id != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id) {
|
|
t.Errorf("Expected GetGroup to return a valid group")
|
|
}
|
|
g, err = b.GetGroup(context.Background(), "group", queryParamExpand)
|
|
switch {
|
|
case err != nil:
|
|
t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
|
|
case g.GetId() != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id):
|
|
t.Errorf("Expected GetGroup to return a valid group")
|
|
case len(g.Members) != 1:
|
|
t.Errorf("Expected GetGroup with expand to return one member")
|
|
case g.Members[0].GetId() != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.id):
|
|
t.Errorf("Expected GetGroup with expand to return correct member")
|
|
}
|
|
g, err = b.GetGroup(context.Background(), "group", queryParamSelect)
|
|
switch {
|
|
case err != nil:
|
|
t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
|
|
case g.GetId() != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id):
|
|
t.Errorf("Expected GetGroup to return a valid group")
|
|
case len(g.Members) != 1:
|
|
t.Errorf("Expected GetGroup with expand to return one member")
|
|
case g.Members[0].GetId() != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.id):
|
|
t.Errorf("Expected GetGroup with expand to return correct member")
|
|
}
|
|
}
|
|
|
|
func TestGetGroupReadOnlyBackend(t *testing.T) {
|
|
readOnlyConfig := lconfig
|
|
readOnlyConfig.WriteEnabled = false
|
|
readOnlyConfig.GroupBaseDN = "ou=groups,dc=test"
|
|
readOnlyConfig.GroupCreateBaseDN = "ou=local,ou=group,dc=test"
|
|
localGroupEntry := groupEntry
|
|
localGroupEntry.DN = "cn=local,ou=local,o=base"
|
|
|
|
lm := &mocks.Client{}
|
|
lm.On("Search", groupLookupSearchRequest).Return(&ldap.SearchResult{Entries: []*ldap.Entry{groupEntry}}, nil)
|
|
b, _ := getMockedBackend(lm, readOnlyConfig, &logger)
|
|
g, err := b.GetGroup(context.Background(), "group", url.Values{})
|
|
switch {
|
|
case err != nil:
|
|
t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
|
|
case g.GetId() != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id):
|
|
t.Errorf("Expected GetGroup to return a valid group")
|
|
}
|
|
types := g.GetGroupTypes()
|
|
switch {
|
|
case len(types) == 0:
|
|
t.Errorf("No groupTypes attribute on readonly Group")
|
|
case len(types) > 1:
|
|
t.Errorf("Expected a single groupTypes value on readonly Group")
|
|
case types[0] != "ReadOnly":
|
|
t.Errorf("Expected a groupTypes 'ReadOnly' on readonly Group")
|
|
}
|
|
}
|
|
func TestGetGroupReadOnlySubtree(t *testing.T) {
|
|
readOnlyTreeConfig := lconfig
|
|
readOnlyTreeConfig.GroupCreateBaseDN = "ou=write,ou=groups,dc=test"
|
|
var writeGroupEntry = ldap.NewEntry("cn=group,ou=write,ou=groups,dc=test",
|
|
map[string][]string{
|
|
"cn": {"group"},
|
|
"entryuuid": {"abcd-defg"},
|
|
"member": {
|
|
"uid=user,ou=people,dc=test",
|
|
"uid=invalid,ou=people,dc=test",
|
|
},
|
|
})
|
|
|
|
lm := &mocks.Client{}
|
|
lm.On("Search", groupLookupSearchRequest).Return(&ldap.SearchResult{Entries: []*ldap.Entry{groupEntry}}, nil)
|
|
b, _ := getMockedBackend(lm, readOnlyTreeConfig, &logger)
|
|
g, err := b.GetGroup(context.Background(), "group", url.Values{})
|
|
switch {
|
|
case err != nil:
|
|
t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
|
|
case g.GetId() != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id):
|
|
t.Errorf("Expected GetGroup to return a valid group")
|
|
}
|
|
types := g.GetGroupTypes()
|
|
switch {
|
|
case len(types) == 0:
|
|
t.Errorf("No groupTypes attribute on readonly Group")
|
|
case len(types) > 1:
|
|
t.Errorf("Expected a single groupTypes value on readonly Group")
|
|
case types[0] != "ReadOnly":
|
|
t.Errorf("Expected a groupTypes 'ReadOnly' on readonly Group")
|
|
}
|
|
|
|
lm = &mocks.Client{}
|
|
lm.On("Search", groupLookupSearchRequest).Return(&ldap.SearchResult{Entries: []*ldap.Entry{writeGroupEntry}}, nil)
|
|
b, _ = getMockedBackend(lm, readOnlyTreeConfig, &logger)
|
|
g, err = b.GetGroup(context.Background(), "group", url.Values{})
|
|
switch {
|
|
case err != nil:
|
|
t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
|
|
case g.GetId() != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id):
|
|
t.Errorf("Expected GetGroup to return a valid group")
|
|
}
|
|
types = g.GetGroupTypes()
|
|
if len(types) != 0 {
|
|
t.Errorf("No groupTypes attribute expected on writeable Group")
|
|
}
|
|
}
|
|
|
|
func TestGetGroups(t *testing.T) {
|
|
lm := &mocks.Client{}
|
|
oDataReq, err := godata.ParseRequest(context.Background(), "", url.Values{})
|
|
if err != nil {
|
|
t.Errorf("Expected success, got '%s'", err.Error())
|
|
}
|
|
lm.On("Search", mock.Anything).Return(nil, ldap.NewError(ldap.LDAPResultOperationsError, errors.New("mock")))
|
|
b, _ := getMockedBackend(lm, lconfig, &logger)
|
|
_, err = b.GetGroups(context.Background(), oDataReq)
|
|
assert.ErrorContains(t, err, "itemNotFound:")
|
|
|
|
lm = &mocks.Client{}
|
|
lm.On("Search", mock.Anything).Return(&ldap.SearchResult{}, nil)
|
|
b, _ = getMockedBackend(lm, lconfig, &logger)
|
|
g, err := b.GetGroups(context.Background(), oDataReq)
|
|
if err != nil {
|
|
t.Errorf("Expected success, got '%s'", err.Error())
|
|
} else if g == nil || len(g) != 0 {
|
|
t.Errorf("Expected zero length user slice")
|
|
}
|
|
|
|
lm = &mocks.Client{}
|
|
lm.On("Search", mock.Anything).Return(&ldap.SearchResult{
|
|
Entries: []*ldap.Entry{groupEntry},
|
|
}, nil)
|
|
b, _ = getMockedBackend(lm, lconfig, &logger)
|
|
g, err = b.GetGroups(context.Background(), oDataReq)
|
|
if err != nil {
|
|
t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
|
|
} else if *g[0].Id != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id) {
|
|
t.Errorf("Expected GetGroup to return a valid group")
|
|
}
|
|
|
|
// Mock a valid Search Result with expanded group members
|
|
lm = &mocks.Client{}
|
|
sr2 := &ldap.SearchRequest{
|
|
BaseDN: "uid=user,ou=people,dc=test",
|
|
SizeLimit: 1,
|
|
Filter: "(objectClass=inetOrgPerson)",
|
|
Attributes: ldapUserAttributes,
|
|
Controls: []ldap.Control(nil),
|
|
}
|
|
sr3 := &ldap.SearchRequest{
|
|
BaseDN: "uid=invalid,ou=people,dc=test",
|
|
SizeLimit: 1,
|
|
Filter: "(objectClass=inetOrgPerson)",
|
|
Attributes: ldapUserAttributes,
|
|
Controls: []ldap.Control(nil),
|
|
}
|
|
|
|
for _, param := range []url.Values{queryParamSelect, queryParamExpand} {
|
|
oDataReq, err := godata.ParseRequest(context.Background(), "", param)
|
|
if err != nil {
|
|
t.Errorf("Expected success, got '%s'", err.Error())
|
|
}
|
|
lm.On("Search", groupListSearchRequest).Return(&ldap.SearchResult{Entries: []*ldap.Entry{groupEntry}}, nil)
|
|
lm.On("Search", sr2).Return(&ldap.SearchResult{Entries: []*ldap.Entry{userEntry}}, nil)
|
|
lm.On("Search", sr3).Return(&ldap.SearchResult{Entries: []*ldap.Entry{invalidUserEntry}}, nil)
|
|
b, _ = getMockedBackend(lm, lconfig, &logger)
|
|
g, err = b.GetGroups(context.Background(), oDataReq)
|
|
switch {
|
|
case err != nil:
|
|
t.Errorf("Expected GetGroup to succeed. Got %s", err.Error())
|
|
case g[0].GetId() != groupEntry.GetEqualFoldAttributeValue(b.groupAttributeMap.id):
|
|
t.Errorf("Expected GetGroup to return a valid group")
|
|
case len(g[0].Members) != 1:
|
|
t.Errorf("Expected GetGroup to return group with one member")
|
|
case g[0].Members[0].GetId() != userEntry.GetEqualFoldAttributeValue(b.userAttributeMap.id):
|
|
t.Errorf("Expected GetGroup to return group with correct member")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetGroupsSearch(t *testing.T) {
|
|
lm := &mocks.Client{}
|
|
odataReqDefault, err := godata.ParseRequest(context.Background(), "",
|
|
url.Values{
|
|
"$search": []string{"\"term\""},
|
|
},
|
|
)
|
|
if err != nil {
|
|
t.Errorf("Expected success got '%s'", err.Error())
|
|
}
|
|
|
|
// only match if the filter contains the search term unquoted
|
|
lm.On("Search", mock.MatchedBy(
|
|
func(req *ldap.SearchRequest) bool {
|
|
return req.Filter == "(&(objectClass=groupOfNames)(|(cn=*term*)(entryUUID=*term*)))"
|
|
})).
|
|
Return(&ldap.SearchResult{}, nil)
|
|
b, _ := getMockedBackend(lm, lconfig, &logger)
|
|
g, err := b.GetGroups(context.Background(), odataReqDefault)
|
|
if err != nil {
|
|
t.Errorf("Expected success, got '%s'", err.Error())
|
|
} else if g == nil || len(g) != 0 {
|
|
t.Errorf("Expected zero length user slice")
|
|
}
|
|
}
|
|
func TestUpdateGroupName(t *testing.T) {
|
|
groupDn := "cn=TheGroup,ou=groups,dc=owncloud,dc=com"
|
|
|
|
type args struct {
|
|
groupId string
|
|
groupName string
|
|
newName string
|
|
}
|
|
|
|
type mockInputs struct {
|
|
funcName string
|
|
args []interface{}
|
|
returns []interface{}
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
assertion assert.ErrorAssertionFunc
|
|
ldapMocks []mockInputs
|
|
}{
|
|
{
|
|
name: "Test with no name change",
|
|
args: args{
|
|
groupId: "some-uuid-string",
|
|
newName: "TheGroup",
|
|
},
|
|
assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
|
|
return assert.Nil(t, err, args...)
|
|
},
|
|
ldapMocks: []mockInputs{
|
|
{
|
|
funcName: "Search",
|
|
args: []interface{}{
|
|
ldap.NewSearchRequest(
|
|
"ou=groups,dc=test",
|
|
ldap.ScopeWholeSubtree,
|
|
ldap.NeverDerefAliases, 1, 0, false,
|
|
"(&(objectClass=groupOfNames)(entryUUID=some-uuid-string))",
|
|
[]string{"cn", "entryUUID", "member"},
|
|
nil,
|
|
),
|
|
},
|
|
returns: []interface{}{
|
|
&ldap.SearchResult{
|
|
Entries: []*ldap.Entry{
|
|
{
|
|
DN: groupDn,
|
|
Attributes: []*ldap.EntryAttribute{
|
|
{
|
|
Name: "cn",
|
|
Values: []string{"TheGroup"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Test with name change",
|
|
args: args{
|
|
groupId: "some-uuid-string",
|
|
newName: "TheGroupWithShinyNewName",
|
|
},
|
|
assertion: func(t assert.TestingT, err error, args ...interface{}) bool {
|
|
return assert.Nil(t, err, args...)
|
|
},
|
|
ldapMocks: []mockInputs{
|
|
{
|
|
funcName: "Search",
|
|
args: []interface{}{
|
|
ldap.NewSearchRequest(
|
|
"ou=groups,dc=test",
|
|
ldap.ScopeWholeSubtree,
|
|
ldap.NeverDerefAliases, 1, 0, false,
|
|
"(&(objectClass=groupOfNames)(entryUUID=some-uuid-string))",
|
|
[]string{"cn", "entryUUID", "member"},
|
|
nil,
|
|
),
|
|
},
|
|
returns: []interface{}{
|
|
&ldap.SearchResult{
|
|
Entries: []*ldap.Entry{
|
|
{
|
|
DN: "cn=TheGroup,ou=groups,dc=owncloud,dc=com",
|
|
Attributes: []*ldap.EntryAttribute{
|
|
{
|
|
Name: "cn",
|
|
Values: []string{"TheGroup"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
nil,
|
|
},
|
|
},
|
|
{
|
|
funcName: "ModifyDN",
|
|
args: []interface{}{
|
|
&ldap.ModifyDNRequest{
|
|
DN: groupDn,
|
|
NewRDN: "cn=TheGroupWithShinyNewName",
|
|
DeleteOldRDN: true,
|
|
NewSuperior: "",
|
|
Controls: []ldap.Control(nil),
|
|
},
|
|
},
|
|
returns: []interface{}{
|
|
nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
lm := &mocks.Client{}
|
|
for _, ldapMock := range tt.ldapMocks {
|
|
lm.On(ldapMock.funcName, ldapMock.args...).Return(ldapMock.returns...)
|
|
}
|
|
|
|
ldapConfig := lconfig
|
|
i, _ := getMockedBackend(lm, ldapConfig, &logger)
|
|
|
|
err := i.UpdateGroupName(context.Background(), tt.args.groupId, tt.args.newName)
|
|
tt.assertion(t, err)
|
|
})
|
|
}
|
|
}
|