graph: new config option GRAPH_LDAP_GROUP_CREATE_BASE_DN
By setting GRAPH_LDAP_GROUP_CREATE_BASE_DN a distinct subtree can be configured where new LDAP groups are created. That subtree needs to be subordinate to GRAPH_LDAP_GROUP_BASE_DN. All groups outside for GRAPH_LDAP_GROUP_CREATE_BASE_DN are considered read-only and only groups below that DN can be updated and deleted. This is introduced for a pretty specific usecase where most groups are managed in an external source (e.g. a read-only replica of an LDAP tree). But we still want to allow the local administrator to create groups in a writeable subtree attached to that replica.
This commit is contained in:
committed by
Ralf Haferkamp
parent
65a3fc09ca
commit
120887abcc
@@ -204,10 +204,16 @@ func (i *LDAP) DeleteGroup(ctx context.Context, id string) error {
|
||||
if !i.writeEnabled {
|
||||
return errorcode.New(errorcode.NotAllowed, "server is configured read-only")
|
||||
}
|
||||
|
||||
e, err := i.getLDAPGroupByID(id, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.isLDAPGroupReadOnly(e) {
|
||||
return errorcode.New(errorcode.NotAllowed, "group is read-only")
|
||||
}
|
||||
|
||||
dr := ldap.DelRequest{DN: e.DN}
|
||||
if err = i.conn.Del(&dr); err != nil {
|
||||
return err
|
||||
@@ -219,12 +225,19 @@ func (i *LDAP) DeleteGroup(ctx context.Context, id string) error {
|
||||
func (i *LDAP) UpdateGroupName(ctx context.Context, groupID string, groupName string) error {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("AddMembersToGroup")
|
||||
if !i.writeEnabled {
|
||||
return errorcode.New(errorcode.NotAllowed, "server is configured read-only")
|
||||
}
|
||||
|
||||
ge, err := i.getLDAPGroupByID(groupID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.isLDAPGroupReadOnly(ge) {
|
||||
return errorcode.New(errorcode.NotAllowed, "group is read-only")
|
||||
}
|
||||
|
||||
if ge.GetEqualFoldAttributeValue(i.groupAttributeMap.name) == groupName {
|
||||
return nil
|
||||
}
|
||||
@@ -258,11 +271,18 @@ func (i *LDAP) UpdateGroupName(ctx context.Context, groupID string, groupName st
|
||||
func (i *LDAP) AddMembersToGroup(ctx context.Context, groupID string, memberIDs []string) error {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("AddMembersToGroup")
|
||||
if !i.writeEnabled {
|
||||
return errorcode.New(errorcode.NotAllowed, "server is configured read-only")
|
||||
}
|
||||
ge, err := i.getLDAPGroupByNameOrID(groupID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.isLDAPGroupReadOnly(ge) {
|
||||
return errorcode.New(errorcode.NotAllowed, "group is read-only")
|
||||
}
|
||||
|
||||
mr := ldap.ModifyRequest{DN: ge.DN}
|
||||
// Handle empty groups (using the empty member attribute)
|
||||
current := ge.GetEqualFoldAttributeValues(i.groupAttributeMap.member)
|
||||
@@ -326,11 +346,20 @@ func (i *LDAP) AddMembersToGroup(ctx context.Context, groupID string, memberIDs
|
||||
func (i *LDAP) RemoveMemberFromGroup(ctx context.Context, groupID string, memberID string) error {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("RemoveMemberFromGroup")
|
||||
if !i.writeEnabled {
|
||||
return errorcode.New(errorcode.NotAllowed, "server is configured read-only")
|
||||
}
|
||||
|
||||
ge, err := i.getLDAPGroupByID(groupID, true)
|
||||
if err != nil {
|
||||
logger.Debug().Str("backend", "ldap").Str("groupID", groupID).Msg("Error looking up group")
|
||||
return err
|
||||
}
|
||||
|
||||
if i.isLDAPGroupReadOnly(ge) {
|
||||
return errorcode.New(errorcode.NotAllowed, "group is read-only")
|
||||
}
|
||||
|
||||
me, err := i.getLDAPUserByID(memberID)
|
||||
if err != nil {
|
||||
logger.Debug().Str("backend", "ldap").Str("memberID", memberID).Msg("Error looking up group member")
|
||||
@@ -345,7 +374,7 @@ func (i *LDAP) RemoveMemberFromGroup(ctx context.Context, groupID string, member
|
||||
}
|
||||
|
||||
func (i *LDAP) groupToAddRequest(group libregraph.Group) (*ldap.AddRequest, error) {
|
||||
ar := ldap.NewAddRequest(i.getGroupLDAPDN(group), nil)
|
||||
ar := ldap.NewAddRequest(i.getGroupCreateLDAPDN(group), nil)
|
||||
|
||||
attrMap, err := i.groupToLDAPAttrValues(group)
|
||||
if err != nil {
|
||||
@@ -357,12 +386,12 @@ func (i *LDAP) groupToAddRequest(group libregraph.Group) (*ldap.AddRequest, erro
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
func (i *LDAP) getGroupLDAPDN(group libregraph.Group) string {
|
||||
func (i *LDAP) getGroupCreateLDAPDN(group libregraph.Group) string {
|
||||
attributeTypeAndValue := ldap.AttributeTypeAndValue{
|
||||
Type: "cn",
|
||||
Value: group.GetDisplayName(),
|
||||
}
|
||||
return fmt.Sprintf("%s,%s", attributeTypeAndValue.String(), i.groupBaseDN)
|
||||
return fmt.Sprintf("%s,%s", attributeTypeAndValue.String(), i.groupCreateBaseDN)
|
||||
}
|
||||
|
||||
func (i *LDAP) groupToLDAPAttrValues(group libregraph.Group) (map[string][]string, error) {
|
||||
@@ -482,17 +511,43 @@ func (i *LDAP) getGroupsForUser(dn string) ([]*ldap.Entry, error) {
|
||||
func (i *LDAP) createGroupModelFromLDAP(e *ldap.Entry) *libregraph.Group {
|
||||
name := e.GetEqualFoldAttributeValue(i.groupAttributeMap.name)
|
||||
id := e.GetEqualFoldAttributeValue(i.groupAttributeMap.id)
|
||||
groupTypes := []string{}
|
||||
|
||||
if i.isLDAPGroupReadOnly(e) {
|
||||
groupTypes = []string{"ReadOnly"}
|
||||
}
|
||||
|
||||
if id != "" && name != "" {
|
||||
return &libregraph.Group{
|
||||
DisplayName: &name,
|
||||
Id: &id,
|
||||
GroupTypes: groupTypes,
|
||||
}
|
||||
}
|
||||
i.logger.Warn().Str("dn", e.DN).Msg("Group is missing name or id")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *LDAP) isLDAPGroupReadOnly(e *ldap.Entry) bool {
|
||||
if !i.writeEnabled {
|
||||
return true
|
||||
}
|
||||
|
||||
groupDN, err := ldap.ParseDN(e.DN)
|
||||
if err != nil {
|
||||
i.logger.Warn().Err(err).Str("dn", e.DN).Msg("Failed to parse DN")
|
||||
return false
|
||||
}
|
||||
|
||||
baseDN, err := ldap.ParseDN(i.groupCreateBaseDN)
|
||||
if err != nil {
|
||||
i.logger.Warn().Err(err).Str("dn", i.groupCreateBaseDN).Msg("Failed to parse DN")
|
||||
return false
|
||||
}
|
||||
|
||||
return !baseDN.AncestorOfFold(groupDN)
|
||||
}
|
||||
|
||||
func (i *LDAP) groupsFromLDAPEntries(e []*ldap.Entry) []libregraph.Group {
|
||||
groups := make([]libregraph.Group, 0, len(e))
|
||||
for _, g := range e {
|
||||
|
||||
Reference in New Issue
Block a user