mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-06 03:58:55 -06:00
graph: Initial LDAP support for /education/users
This implements GetEducationUser, GetEducationUsers, DeleteEducationUser and CreateEducationUser methods for the LDAP backend. It's still very basic and no fancy filtering or expanding is there yet.
This commit is contained in:
committed by
Ralf Haferkamp
parent
78637a2f00
commit
80a2c72491
@@ -60,6 +60,8 @@ type groupAttributeMap struct {
|
||||
memberSyntax string
|
||||
}
|
||||
|
||||
type ldapAttributeValues map[string][]string
|
||||
|
||||
func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LDAP, error) {
|
||||
if config.UserDisplayNameAttribute == "" || config.UserIDAttribute == "" ||
|
||||
config.UserEmailAttribute == "" || config.UserNameAttribute == "" {
|
||||
@@ -126,54 +128,13 @@ func (i *LDAP) CreateUser(ctx context.Context, user libregraph.User) (*libregrap
|
||||
if !i.writeEnabled {
|
||||
return nil, errReadOnly
|
||||
}
|
||||
ar := ldap.AddRequest{
|
||||
DN: fmt.Sprintf("uid=%s,%s", oldap.EscapeDNAttributeValue(*user.OnPremisesSamAccountName), i.userBaseDN),
|
||||
Attributes: []ldap.Attribute{
|
||||
// inetOrgPerson requires "cn"
|
||||
{
|
||||
Type: "cn",
|
||||
Vals: []string{*user.OnPremisesSamAccountName},
|
||||
},
|
||||
{
|
||||
Type: i.userAttributeMap.mail,
|
||||
Vals: []string{*user.Mail},
|
||||
},
|
||||
{
|
||||
Type: i.userAttributeMap.userName,
|
||||
Vals: []string{*user.OnPremisesSamAccountName},
|
||||
},
|
||||
{
|
||||
Type: i.userAttributeMap.displayName,
|
||||
Vals: []string{*user.DisplayName},
|
||||
},
|
||||
},
|
||||
|
||||
ar, err := i.userToAddRequest(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objectClasses := []string{"inetOrgPerson", "organizationalPerson", "person", "top"}
|
||||
|
||||
if !i.usePwModifyExOp && user.PasswordProfile != nil && user.PasswordProfile.Password != nil {
|
||||
// Depending on the LDAP server implementation this might cause the
|
||||
// password to be stored in cleartext in the LDAP database. Using the
|
||||
// "Password Modify LDAP Extended Operation" is recommended.
|
||||
ar.Attribute("userPassword", []string{*user.PasswordProfile.Password})
|
||||
}
|
||||
if !i.useServerUUID {
|
||||
ar.Attribute("owncloudUUID", []string{uuid.Must(uuid.NewV4()).String()})
|
||||
objectClasses = append(objectClasses, "owncloud")
|
||||
}
|
||||
ar.Attribute("objectClass", objectClasses)
|
||||
|
||||
// inetOrgPerson requires "sn" to be set. Set it to the Username if
|
||||
// Surname is not set in the Request
|
||||
var sn string
|
||||
if user.Surname != nil && *user.Surname != "" {
|
||||
sn = *user.Surname
|
||||
} else {
|
||||
sn = *user.OnPremisesSamAccountName
|
||||
}
|
||||
ar.Attribute("sn", []string{sn})
|
||||
|
||||
if err := i.conn.Add(&ar); err != nil {
|
||||
if err := i.conn.Add(ar); err != nil {
|
||||
var lerr *ldap.Error
|
||||
logger.Debug().Err(err).Msg("error adding user")
|
||||
if errors.As(err, &lerr) {
|
||||
@@ -366,6 +327,40 @@ func (i *LDAP) getEntryByDN(dn string, attrs []string, filter string) (*ldap.Ent
|
||||
return res.Entries[0], nil
|
||||
}
|
||||
|
||||
func (i *LDAP) searchLDAPEntryByFilter(basedn string, attrs []string, filter string) (*ldap.Entry, error) {
|
||||
if filter == "" {
|
||||
filter = "(objectclass=*)"
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
basedn,
|
||||
ldap.ScopeWholeSubtree,
|
||||
ldap.NeverDerefAliases, 1, 0, false,
|
||||
filter,
|
||||
attrs,
|
||||
nil,
|
||||
)
|
||||
|
||||
i.logger.Debug().Str("backend", "ldap").
|
||||
Str("base", searchRequest.BaseDN).
|
||||
Str("filter", searchRequest.Filter).
|
||||
Int("scope", searchRequest.Scope).
|
||||
Int("sizelimit", searchRequest.SizeLimit).
|
||||
Interface("attributes", searchRequest.Attributes).
|
||||
Msg("getEntryByFilter")
|
||||
res, err := i.conn.Search(searchRequest)
|
||||
|
||||
if err != nil {
|
||||
i.logger.Error().Err(err).Str("backend", "ldap").Str("dn", basedn).Str("filter", filter).Msg("Search user by filter failed")
|
||||
return nil, errorcode.New(errorcode.ItemNotFound, err.Error())
|
||||
}
|
||||
if len(res.Entries) == 0 {
|
||||
return nil, errNotFound
|
||||
}
|
||||
|
||||
return res.Entries[0], nil
|
||||
}
|
||||
|
||||
func (i *LDAP) getLDAPUserByID(id string) (*ldap.Entry, error) {
|
||||
id = ldap.EscapeFilter(id)
|
||||
filter := fmt.Sprintf("(%s=%s)", i.userAttributeMap.id, id)
|
||||
@@ -379,42 +374,14 @@ func (i *LDAP) getLDAPUserByNameOrID(nameOrID string) (*ldap.Entry, error) {
|
||||
}
|
||||
|
||||
func (i *LDAP) getLDAPUserByFilter(filter string) (*ldap.Entry, error) {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
i.userBaseDN, i.userScope, ldap.NeverDerefAliases, 1, 0, false,
|
||||
fmt.Sprintf("(&%s(objectClass=%s)%s)", i.userFilter, i.userObjectClass, filter),
|
||||
[]string{
|
||||
i.userAttributeMap.displayName,
|
||||
i.userAttributeMap.id,
|
||||
i.userAttributeMap.mail,
|
||||
i.userAttributeMap.userName,
|
||||
},
|
||||
nil,
|
||||
)
|
||||
i.logger.Debug().Str("backend", "ldap").
|
||||
Str("base", searchRequest.BaseDN).
|
||||
Str("filter", searchRequest.Filter).
|
||||
Int("scope", searchRequest.Scope).
|
||||
Int("sizelimit", searchRequest.SizeLimit).
|
||||
Interface("attributes", searchRequest.Attributes).
|
||||
Msg("getLDAPUserByFilter")
|
||||
res, err := i.conn.Search(searchRequest)
|
||||
|
||||
if err != nil {
|
||||
var errmsg string
|
||||
if lerr, ok := err.(*ldap.Error); ok {
|
||||
if lerr.ResultCode == ldap.LDAPResultSizeLimitExceeded {
|
||||
errmsg = fmt.Sprintf("too many results searching for user '%s'", filter)
|
||||
i.logger.Debug().Str("backend", "ldap").Err(lerr).
|
||||
Str("userfilter", filter).Msg("too many results searching for user")
|
||||
}
|
||||
}
|
||||
return nil, errorcode.New(errorcode.ItemNotFound, errmsg)
|
||||
filter = fmt.Sprintf("(&%s(objectClass=%s)%s)", i.userFilter, i.userObjectClass, filter)
|
||||
attrs := []string{
|
||||
i.userAttributeMap.displayName,
|
||||
i.userAttributeMap.id,
|
||||
i.userAttributeMap.mail,
|
||||
i.userAttributeMap.userName,
|
||||
}
|
||||
if len(res.Entries) == 0 {
|
||||
return nil, errNotFound
|
||||
}
|
||||
|
||||
return res.Entries[0], nil
|
||||
return i.searchLDAPEntryByFilter(i.userBaseDN, attrs, filter)
|
||||
}
|
||||
|
||||
func (i *LDAP) GetUser(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.User, error) {
|
||||
@@ -1010,6 +977,56 @@ func (i *LDAP) groupsFromLDAPEntries(e []*ldap.Entry) []libregraph.Group {
|
||||
return groups
|
||||
}
|
||||
|
||||
func (i *LDAP) userToLDAPAttrValues(user libregraph.User) (map[string][]string, error) {
|
||||
attrs := map[string][]string{
|
||||
i.userAttributeMap.displayName: {user.GetDisplayName()},
|
||||
i.userAttributeMap.userName: {user.GetOnPremisesSamAccountName()},
|
||||
i.userAttributeMap.mail: {user.GetMail()},
|
||||
"objectClass": {"inetOrgPerson", "organizationalPerson", "person", "top"},
|
||||
"cn": {user.GetOnPremisesSamAccountName()},
|
||||
}
|
||||
|
||||
if !i.useServerUUID {
|
||||
attrs["owncloudUUID"] = []string{uuid.Must(uuid.NewV4()).String()}
|
||||
attrs["objectClass"] = append(attrs["objectClass"], "owncloud")
|
||||
}
|
||||
|
||||
// inetOrgPerson requires "sn" to be set. Set it to the Username if
|
||||
// Surname is not set in the Request
|
||||
var sn string
|
||||
if user.Surname != nil && *user.Surname != "" {
|
||||
sn = *user.Surname
|
||||
} else {
|
||||
sn = *user.OnPremisesSamAccountName
|
||||
}
|
||||
attrs["sn"] = []string{sn}
|
||||
|
||||
if !i.usePwModifyExOp && user.PasswordProfile != nil && user.PasswordProfile.Password != nil {
|
||||
// Depending on the LDAP server implementation this might cause the
|
||||
// password to be stored in cleartext in the LDAP database. Using the
|
||||
// "Password Modify LDAP Extended Operation" is recommended.
|
||||
attrs["userPassword"] = []string{*user.PasswordProfile.Password}
|
||||
}
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func (i *LDAP) getUserLDAPDN(user libregraph.User) string {
|
||||
return fmt.Sprintf("uid=%s,%s", oldap.EscapeDNAttributeValue(*user.OnPremisesSamAccountName), i.userBaseDN)
|
||||
}
|
||||
|
||||
func (i *LDAP) userToAddRequest(user libregraph.User) (*ldap.AddRequest, error) {
|
||||
ar := ldap.NewAddRequest(i.getUserLDAPDN(user), nil)
|
||||
|
||||
attrMap, err := i.userToLDAPAttrValues(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for attrType, values := range attrMap {
|
||||
ar.Attribute(attrType, values)
|
||||
}
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
func pointerOrNil(val string) *string {
|
||||
if val == "" {
|
||||
return nil
|
||||
|
||||
@@ -2,19 +2,78 @@ package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
)
|
||||
|
||||
type educationUserAttributeMap struct {
|
||||
identities string
|
||||
primaryRole string
|
||||
}
|
||||
|
||||
func newEducationUserAttributeMap() educationUserAttributeMap {
|
||||
return educationUserAttributeMap{
|
||||
identities: "oCExternalIdentity",
|
||||
primaryRole: "userClass",
|
||||
}
|
||||
}
|
||||
|
||||
// CreateEducationUser creates a given education user in the identity backend.
|
||||
func (i *LDAP) CreateEducationUser(ctx context.Context, user libregraph.EducationUser) (*libregraph.EducationUser, error) {
|
||||
return nil, errNotImplemented
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("CreateEducationUser")
|
||||
if !i.writeEnabled {
|
||||
return nil, errReadOnly
|
||||
}
|
||||
|
||||
ar, err := i.educationUserToAddRequest(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := i.conn.Add(ar); err != nil {
|
||||
var lerr *ldap.Error
|
||||
logger.Debug().Err(err).Msg("error adding user")
|
||||
if errors.As(err, &lerr) {
|
||||
if lerr.ResultCode == ldap.LDAPResultEntryAlreadyExists {
|
||||
err = errorcode.New(errorcode.NameAlreadyExists, lerr.Error())
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read back user from LDAP to get the generated UUID
|
||||
e, err := i.getEducationUserByDN(ar.DN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.createEducationUserModelFromLDAP(e), nil
|
||||
}
|
||||
|
||||
// DeleteEducationUser deletes a given educationuser, identified by username or id, from the backend
|
||||
func (i *LDAP) DeleteEducationUser(ctx context.Context, nameOrID string) error {
|
||||
return errNotImplemented
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("DeleteEducationUser")
|
||||
if !i.writeEnabled {
|
||||
return errReadOnly
|
||||
}
|
||||
// TODO, implement a proper lookup for education Users here
|
||||
e, err := i.getEducationUserByNameOrID(nameOrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dr := ldap.DelRequest{DN: e.DN}
|
||||
if err = i.conn.Del(&dr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEducationUser applies changes to given education user, identified by username or id
|
||||
@@ -24,10 +83,192 @@ func (i *LDAP) UpdateEducationUser(ctx context.Context, nameOrID string, user li
|
||||
|
||||
// GetEducationUser implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetEducationUser(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.EducationUser, error) {
|
||||
return nil, errNotImplemented
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("GetEducationUser")
|
||||
e, err := i.getEducationUserByNameOrID(nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := i.createEducationUserModelFromLDAP(e)
|
||||
if u == nil {
|
||||
return nil, errNotFound
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// GetEducationUsers implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetEducationUsers(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationUser, error) {
|
||||
return nil, errNotImplemented
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("GetEducationUsers")
|
||||
|
||||
search := queryParam.Get("search")
|
||||
if search == "" {
|
||||
search = queryParam.Get("$search")
|
||||
}
|
||||
var userFilter string
|
||||
if search != "" {
|
||||
search = ldap.EscapeFilter(search)
|
||||
userFilter = fmt.Sprintf(
|
||||
"(|(%s=%s*)(%s=%s*)(%s=%s*))",
|
||||
i.userAttributeMap.userName, search,
|
||||
i.userAttributeMap.mail, search,
|
||||
i.userAttributeMap.displayName, search,
|
||||
)
|
||||
}
|
||||
|
||||
if userFilter == "" && i.userFilter == "" {
|
||||
userFilter = fmt.Sprintf("(objectClass=%s)", i.educationConfig.userObjectClass)
|
||||
} else {
|
||||
userFilter = fmt.Sprintf("(&%s(objectClass=%s)%s)", i.userFilter, i.educationConfig.userObjectClass, userFilter)
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
i.userBaseDN,
|
||||
i.userScope,
|
||||
ldap.NeverDerefAliases, 0, 0, false,
|
||||
userFilter,
|
||||
i.getEducationUserAttrTypes(),
|
||||
nil,
|
||||
)
|
||||
logger.Debug().Str("backend", "ldap").
|
||||
Str("base", searchRequest.BaseDN).
|
||||
Str("filter", searchRequest.Filter).
|
||||
Int("scope", searchRequest.Scope).
|
||||
Int("sizelimit", searchRequest.SizeLimit).
|
||||
Interface("attributes", searchRequest.Attributes).
|
||||
Msg("GetEducationUsers")
|
||||
res, err := i.conn.Search(searchRequest)
|
||||
if err != nil {
|
||||
return nil, errorcode.New(errorcode.ItemNotFound, err.Error())
|
||||
}
|
||||
|
||||
users := make([]*libregraph.EducationUser, 0, len(res.Entries))
|
||||
|
||||
for _, e := range res.Entries {
|
||||
u := i.createEducationUserModelFromLDAP(e)
|
||||
// Skip invalid LDAP users
|
||||
if u == nil {
|
||||
continue
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (i *LDAP) educationUserToUser(eduUser libregraph.EducationUser) *libregraph.User {
|
||||
user := libregraph.NewUser()
|
||||
user.OnPremisesSamAccountName = eduUser.OnPremisesSamAccountName
|
||||
user.Surname = eduUser.Surname
|
||||
user.AccountEnabled = eduUser.AccountEnabled
|
||||
user.GivenName = eduUser.GivenName
|
||||
user.DisplayName = eduUser.DisplayName
|
||||
user.Mail = eduUser.Mail
|
||||
return user
|
||||
}
|
||||
func (i *LDAP) userToEducationUser(user libregraph.User, e *ldap.Entry) *libregraph.EducationUser {
|
||||
eduUser := libregraph.NewEducationUser()
|
||||
eduUser.Id = user.Id
|
||||
eduUser.OnPremisesSamAccountName = user.OnPremisesSamAccountName
|
||||
eduUser.Surname = user.Surname
|
||||
eduUser.AccountEnabled = user.AccountEnabled
|
||||
eduUser.GivenName = user.GivenName
|
||||
eduUser.DisplayName = user.DisplayName
|
||||
eduUser.Mail = user.Mail
|
||||
|
||||
if e != nil {
|
||||
// Set the education User specific Attributes from the supplied LDAP Entry
|
||||
if primaryRole := e.GetEqualFoldAttributeValue(i.educationConfig.userAttributeMap.primaryRole); primaryRole != "" {
|
||||
eduUser.SetPrimaryRole(primaryRole)
|
||||
}
|
||||
var identities []libregraph.ObjectIdentity
|
||||
for _, identityStr := range e.GetEqualFoldAttributeValues(i.educationConfig.userAttributeMap.identities) {
|
||||
parts := strings.SplitN(identityStr, "$", 3)
|
||||
identity := libregraph.NewObjectIdentity()
|
||||
identity.SetIssuer(strings.TrimSpace(parts[1]))
|
||||
identity.SetIssuerAssignedId(strings.TrimSpace(parts[2]))
|
||||
identities = append(identities, *identity)
|
||||
}
|
||||
if len(identities) > 0 {
|
||||
eduUser.SetIdentities(identities)
|
||||
}
|
||||
}
|
||||
|
||||
return eduUser
|
||||
}
|
||||
|
||||
func (i *LDAP) educationUserToLDAPAttrValues(user libregraph.EducationUser, attrs ldapAttributeValues) (ldapAttributeValues, error) {
|
||||
if role, ok := user.GetPrimaryRoleOk(); ok {
|
||||
attrs[i.educationConfig.userAttributeMap.primaryRole] = []string{*role}
|
||||
}
|
||||
if identities, ok := user.GetIdentitiesOk(); ok {
|
||||
for _, identity := range identities {
|
||||
// TODO add support for the "signInType" of objectIdentity
|
||||
if identity.GetIssuer() == "" || identity.GetIssuerAssignedId() == "" {
|
||||
return nil, fmt.Errorf("missing Attribute for objectIdentity")
|
||||
}
|
||||
identityStr := fmt.Sprintf(" $ %s $ %s", identity.GetIssuer(), identity.GetIssuerAssignedId())
|
||||
attrs[i.educationConfig.userAttributeMap.identities] = append(
|
||||
attrs[i.educationConfig.userAttributeMap.identities],
|
||||
identityStr,
|
||||
)
|
||||
}
|
||||
}
|
||||
attrs["objectClass"] = append(attrs["objectClass"], i.educationConfig.userObjectClass)
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func (i *LDAP) educationUserToAddRequest(user libregraph.EducationUser) (*ldap.AddRequest, error) {
|
||||
plainUser := i.educationUserToUser(user)
|
||||
ldapAttrs, err := i.userToLDAPAttrValues(*plainUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ldapAttrs, err = i.educationUserToLDAPAttrValues(user, ldapAttrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ar := ldap.NewAddRequest(i.getUserLDAPDN(*plainUser), nil)
|
||||
|
||||
for attrType, values := range ldapAttrs {
|
||||
ar.Attribute(attrType, values)
|
||||
}
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
func (i *LDAP) createEducationUserModelFromLDAP(e *ldap.Entry) *libregraph.EducationUser {
|
||||
user := i.createUserModelFromLDAP(e)
|
||||
return i.userToEducationUser(*user, e)
|
||||
}
|
||||
|
||||
func (i *LDAP) getEducationUserAttrTypes() []string {
|
||||
return []string{
|
||||
i.userAttributeMap.displayName,
|
||||
i.userAttributeMap.id,
|
||||
i.userAttributeMap.mail,
|
||||
i.userAttributeMap.userName,
|
||||
i.educationConfig.userAttributeMap.identities,
|
||||
i.educationConfig.userAttributeMap.primaryRole,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *LDAP) getEducationUserByDN(dn string) (*ldap.Entry, error) {
|
||||
filter := fmt.Sprintf("(objectClass=%s)", i.educationConfig.userObjectClass)
|
||||
|
||||
if i.userFilter != "" {
|
||||
filter = fmt.Sprintf("(&%s(%s))", filter, i.userFilter)
|
||||
}
|
||||
|
||||
return i.getEntryByDN(dn, i.getEducationUserAttrTypes(), filter)
|
||||
}
|
||||
|
||||
func (i *LDAP) getEducationUserByNameOrID(nameOrID string) (*ldap.Entry, error) {
|
||||
nameOrID = ldap.EscapeFilter(nameOrID)
|
||||
filter := fmt.Sprintf("(|(%s=%s)(%s=%s))", i.userAttributeMap.userName, nameOrID, i.userAttributeMap.id, nameOrID)
|
||||
return i.getEducationUserByFilter(filter)
|
||||
}
|
||||
|
||||
func (i *LDAP) getEducationUserByFilter(filter string) (*ldap.Entry, error) {
|
||||
filter = fmt.Sprintf("(&%s(objectClass=%s)%s)", i.userFilter, i.educationConfig.userObjectClass, filter)
|
||||
return i.searchLDAPEntryByFilter(i.userBaseDN, i.getEducationUserAttrTypes(), filter)
|
||||
}
|
||||
|
||||
132
services/graph/pkg/identity/ldap_education_user_test.go
Normal file
132
services/graph/pkg/identity/ldap_education_user_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/mocks"
|
||||
"github.com/test-go/testify/assert"
|
||||
"github.com/test-go/testify/mock"
|
||||
)
|
||||
|
||||
var eduUserEntry = ldap.NewEntry("uid=user,ou=people,dc=test",
|
||||
map[string][]string{
|
||||
"uid": {"testuser"},
|
||||
"displayname": {"Test User"},
|
||||
"mail": {"user@example"},
|
||||
"entryuuid": {"abcd-defg"},
|
||||
"userClass": {"student"},
|
||||
"oCExternalIdentity": {
|
||||
"$ http://idp $ testuser",
|
||||
"xxx $ http://idpnew $ xxxxx-xxxxx-xxxxx",
|
||||
},
|
||||
})
|
||||
|
||||
var sr1 *ldap.SearchRequest = &ldap.SearchRequest{
|
||||
BaseDN: "ou=people,dc=test",
|
||||
Scope: 2,
|
||||
SizeLimit: 1,
|
||||
Filter: "(&(objectClass=ocEducationUser)(|(uid=abcd-defg)(entryUUID=abcd-defg)))",
|
||||
Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass"},
|
||||
Controls: []ldap.Control(nil),
|
||||
}
|
||||
var sr2 *ldap.SearchRequest = &ldap.SearchRequest{
|
||||
BaseDN: "ou=people,dc=test",
|
||||
Scope: 2,
|
||||
SizeLimit: 1,
|
||||
Filter: "(&(objectClass=ocEducationUser)(|(uid=xxxx-xxxx)(entryUUID=xxxx-xxxx)))",
|
||||
Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass"},
|
||||
Controls: []ldap.Control(nil),
|
||||
}
|
||||
|
||||
func TestCreateEducationUser(t *testing.T) {
|
||||
lm := &mocks.Client{}
|
||||
b, err := getMockedBackend(lm, eduConfig, &logger)
|
||||
assert.Nil(t, err)
|
||||
//assert.NotEqual(t, "", b.educationConfig.schoolObjectClass)
|
||||
lm.On("Add", mock.Anything).Return(nil)
|
||||
|
||||
lm.On("Search", mock.Anything).
|
||||
Return(
|
||||
&ldap.SearchResult{
|
||||
Entries: []*ldap.Entry{
|
||||
eduUserEntry,
|
||||
},
|
||||
},
|
||||
nil)
|
||||
user := libregraph.NewEducationUser()
|
||||
user.SetDisplayName("Test User")
|
||||
user.SetOnPremisesSamAccountName("testuser")
|
||||
user.SetMail("testuser@example.org")
|
||||
user.SetPrimaryRole("student")
|
||||
eduUser, err := b.CreateEducationUser(context.Background(), *user)
|
||||
lm.AssertNumberOfCalls(t, "Add", 1)
|
||||
lm.AssertNumberOfCalls(t, "Search", 1)
|
||||
assert.NotNil(t, eduUser)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, eduUser.GetDisplayName(), user.GetDisplayName())
|
||||
assert.Equal(t, eduUser.GetOnPremisesSamAccountName(), user.GetOnPremisesSamAccountName())
|
||||
assert.Equal(t, "abcd-defg", eduUser.GetId())
|
||||
assert.Equal(t, eduUser.GetPrimaryRole(), user.GetPrimaryRole())
|
||||
}
|
||||
|
||||
func TestDeleteEducationUser(t *testing.T) {
|
||||
lm := &mocks.Client{}
|
||||
|
||||
lm.On("Search", sr1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{eduUserEntry}}, nil)
|
||||
lm.On("Search", sr2).Return(&ldap.SearchResult{Entries: []*ldap.Entry{}}, nil)
|
||||
dr1 := &ldap.DelRequest{
|
||||
DN: "uid=user,ou=people,dc=test",
|
||||
}
|
||||
lm.On("Del", dr1).Return(nil)
|
||||
b, err := getMockedBackend(lm, eduConfig, &logger)
|
||||
assert.Nil(t, err)
|
||||
err = b.DeleteEducationUser(context.Background(), "abcd-defg")
|
||||
lm.AssertNumberOfCalls(t, "Search", 1)
|
||||
lm.AssertNumberOfCalls(t, "Del", 1)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = b.DeleteEducationUser(context.Background(), "xxxx-xxxx")
|
||||
lm.AssertNumberOfCalls(t, "Search", 2)
|
||||
lm.AssertNumberOfCalls(t, "Del", 1)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "itemNotFound", err.Error())
|
||||
}
|
||||
|
||||
func TestGetEducationUser(t *testing.T) {
|
||||
lm := &mocks.Client{}
|
||||
lm.On("Search", sr1).Return(&ldap.SearchResult{Entries: []*ldap.Entry{eduUserEntry}}, nil)
|
||||
lm.On("Search", sr2).Return(&ldap.SearchResult{Entries: []*ldap.Entry{}}, nil)
|
||||
b, err := getMockedBackend(lm, eduConfig, &logger)
|
||||
assert.Nil(t, err)
|
||||
user, err := b.GetEducationUser(context.Background(), "abcd-defg", nil)
|
||||
lm.AssertNumberOfCalls(t, "Search", 1)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Test User", user.GetDisplayName())
|
||||
assert.Equal(t, "abcd-defg", user.GetId())
|
||||
|
||||
user, err = b.GetEducationUser(context.Background(), "xxxx-xxxx", nil)
|
||||
lm.AssertNumberOfCalls(t, "Search", 2)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "itemNotFound", err.Error())
|
||||
}
|
||||
|
||||
func TestGetEducationUsers(t *testing.T) {
|
||||
lm := &mocks.Client{}
|
||||
sr := &ldap.SearchRequest{
|
||||
BaseDN: "ou=people,dc=test",
|
||||
Scope: 2,
|
||||
SizeLimit: 0,
|
||||
Filter: "(objectClass=ocEducationUser)",
|
||||
Attributes: []string{"displayname", "entryUUID", "mail", "uid", "oCExternalIdentity", "userClass"},
|
||||
Controls: []ldap.Control(nil),
|
||||
}
|
||||
lm.On("Search", sr).Return(&ldap.SearchResult{Entries: []*ldap.Entry{eduUserEntry}}, nil)
|
||||
b, err := getMockedBackend(lm, eduConfig, &logger)
|
||||
assert.Nil(t, err)
|
||||
_, err = b.GetEducationUsers(context.Background(), nil)
|
||||
lm.AssertNumberOfCalls(t, "Search", 1)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@@ -20,6 +20,9 @@ type educationConfig struct {
|
||||
schoolObjectClass string
|
||||
schoolScope int
|
||||
schoolAttributeMap schoolAttributeMap
|
||||
|
||||
userObjectClass string
|
||||
userAttributeMap educationUserAttributeMap
|
||||
}
|
||||
|
||||
type schoolAttributeMap struct {
|
||||
@@ -33,6 +36,9 @@ func defaultEducationConfig() educationConfig {
|
||||
schoolObjectClass: "ocEducationSchool",
|
||||
schoolScope: ldap.ScopeWholeSubtree,
|
||||
schoolAttributeMap: newSchoolAttributeMap(),
|
||||
|
||||
userObjectClass: "ocEducationUser",
|
||||
userAttributeMap: newEducationUserAttributeMap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user