@ -58,7 +58,16 @@ func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (in
return ldap . LDAPResultEntryAlreadyExists , nil
}
// TODO: check that parent object exists
// Check that parent object exists
parentDn := unparseDN ( dnSplit [ 1 : ] )
parentExists , err := server . objectExists ( parentDn )
if err != nil {
return ldap . LDAPResultOperationsError , err
}
if ! parentExists {
return ldap . LDAPResultNoSuchObject , fmt . Errorf (
"Parent object %s does not exist" , parentDn )
}
// If adding a group, track of who the members will be so that their memberOf field can be updated later
members := [ ] string { }
@ -77,8 +86,9 @@ func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (in
if err != nil {
return ldap . LDAPResultObjectClassViolation , err
}
// If they are writing a member key, we have to check they are adding valid members
if strings . EqualFold ( key , ATTR_MEMBER ) {
// If they are writing a member list, we have to check they are adding valid members
// Also, rewrite member list to use canonical DN syntax (no spaces, all lowercase)
for _ , member := range vals_str {
member_canonical , err := server . checkDN ( member , false )
if err != nil {
@ -93,19 +103,31 @@ func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (in
"Cannot add %s to members, it does not exist!" ,
member_canonical )
}
members = append ( members , member_canonical )
}
members = append ( members , vals_str ... )
}
if prev , ok := entry [ key ] ; ok {
entry [ key ] = append ( prev , vals_str ... )
entry [ key ] = members
} else {
entry [ key ] = vals_str
if prev , ok := entry [ key ] ; ok {
entry [ key ] = append ( prev , vals_str ... )
} else {
entry [ key ] = vals_str
}
}
}
if _ , ok := entry [ ATTR_OBJECTCLASS ] ; ! ok {
// Ensure object has at least one objectclass value
hasObjectClass := false
for k := range entry {
if strings . EqualFold ( k , ATTR_OBJECTCLASS ) {
hasObjectClass = true
break
}
}
if ! hasObjectClass {
entry [ ATTR_OBJECTCLASS ] = [ ] string { "top" }
}
// Write system attributes
entry [ ATTR_CREATORSNAME ] = [ ] string { state . login . user }
entry [ ATTR_CREATETIMESTAMP ] = [ ] string { genTimestamp ( ) }
entry [ ATTR_ENTRYUUID ] = [ ] string { genUuid ( ) }
@ -306,11 +328,25 @@ func (server *Server) handleModifyInternal(state *State, r *message.ModifyReques
addMembers , delMembers := [ ] string { } , [ ] string { }
// Produce new entry values to be saved
newEntry := Entry { }
entry := Entry { }
for _ , change := range r . Changes ( ) {
attr := string ( change . Modification ( ) . Type_ ( ) )
values := change . Modification ( ) . Vals ( )
changeValues := [ ] string { }
for _ , v := range change . Modification ( ) . Vals ( ) {
changeValues = append ( changeValues , string ( v ) )
}
// If we already had an attribute with this name before,
// make sure we are using the same lowercase/uppercase
for prevAttr := range prevEntry {
if strings . EqualFold ( attr , prevAttr ) {
attr = prevAttr
break
}
}
// Check that this attribute is not system-managed thus restricted
err = checkRestrictedAttr ( attr )
if err != nil {
return ldap . LDAPResultObjectClassViolation , err
@ -326,112 +362,100 @@ func (server *Server) handleModifyInternal(state *State, r *message.ModifyReques
return ldap . LDAPResultInsufficientAccessRights , nil
}
if change . Operation ( ) == ldap . ModifyRequestChangeOperationAdd {
newEntry [ attr ] = prevEntry [ attr ]
for _ , val := range values {
present := false
for _ , prevVal := range newEntry [ attr ] {
if prevVal == string ( val ) {
present = true
break
}
// If we are changing ATTR_MEMBER, rewrite all values to canonical form
if strings . EqualFold ( attr , ATTR_MEMBER ) {
for i := range changeValues {
canonical_val , err := server . checkDN ( changeValues [ i ] , false )
if err != nil {
return ldap . LDAPResultInvalidDNSyntax , err
}
if ! present {
newEntry [ attr ] = append ( newEntry [ attr ] , string ( val ) )
changeValues [ i ] = canonical_val
}
}
// If we don't yet have a new value for this attr,
// but one existed before, initialize entry[attr] to the old value
// so that later on what we do is simply modify entry[attr] in place
// (this allows to handle sequences of several changes on the same attr)
if _ , ok := entry [ attr ] ; ! ok {
if _ , ok := prevEntry [ attr ] ; ok {
entry [ attr ] = prevEntry [ attr ]
}
}
// Apply effective modification on entry[attr]
if change . Operation ( ) == ldap . ModifyRequestChangeOperationAdd {
for _ , val := range changeValues {
if ! listContains ( entry [ attr ] , val ) {
entry [ attr ] = append ( entry [ attr ] , val )
if strings . EqualFold ( attr , ATTR_MEMBER ) {
addMembers = append ( addMembers , string ( val ) )
addMembers = append ( addMembers , val )
}
}
}
} else if change . Operation ( ) == ldap . ModifyRequestChangeOperationDelete {
if len ( values ) == 0 {
if len ( changeV alues) == 0 {
// Delete everything
newEntry [ attr ] = [ ] string { }
if strings . EqualFold ( attr , ATTR_MEMBER ) {
delMembers = append ( delMembers , prevEntry [ attr ] ... )
delMembers = append ( delMembers , entry[ attr ] ... )
}
entry [ attr ] = [ ] string { }
} else {
// Delete only those specified
newEntry [ attr ] = [ ] string { }
for _ , prevVal := range prevEntry [ attr ] {
keep := true
for _ , delVal := range values {
if string ( delVal ) == prevVal {
keep = false
break
}
}
if keep {
newEntry [ attr ] = append ( newEntry [ attr ] , prevVal )
newList := [ ] string { }
for _ , prevVal := range entry [ attr ] {
if ! listContains ( changeValues , prevVal ) {
newList = append ( newList , prevVal )
} else {
if strings . EqualFold ( attr , ATTR_MEMBER ) {
delMembers = append ( delMembers , prevVal )
}
}
}
entry [ attr ] = newList
}
} else if change . Operation ( ) == ldap . ModifyRequestChangeOperationReplace {
newEntry [ attr ] = [ ] string { }
for _ , newVal := range values {
newEntry [ attr ] = append ( newEntry [ attr ] , string ( newVal ) )
}
if strings . EqualFold ( attr , ATTR_MEMBER ) {
for _ , newMem := range newEntry [ attr ] {
mustAdd := true
for _ , prevMem := range prevEntry [ attr ] {
if prevMem == newMem {
mustAdd = false
break
}
}
if mustAdd {
for _ , newMem := range changeValues {
if ! listContains ( entry [ attr ] , newMem ) {
addMembers = append ( addMembers , newMem )
}
}
for _ , prevMem := range prevEntry [ attr ] {
mustDel := true
for _ , newMem := range newEntry [ attr ] {
if newMem == prevMem {
mustDel = false
break
}
}
if mustDel {
for _ , prevMem := range entry [ attr ] {
if ! listContains ( changeValues , prevMem ) {
delMembers = append ( delMembers , prevMem )
}
}
}
entry [ attr ] = changeValues
}
}
// Check that added members actually exist
for i := range addMembers {
addMem , err := server . checkDN ( addMembers [ i ] , false )
if err != nil {
return ldap . LDAPResultInvalidDNSyntax , err
}
exists , err := server . objectExists ( addMem )
exists , err := server . objectExists ( addMembers [ i ] )
if err != nil {
return ldap . LDAPResultOperationsError , err
}
if ! exists {
return ldap . LDAPResultNoSuchObject , fmt . Errorf (
"Cannot add member %s, it does not exist" , addMem )
"Cannot add member %s, it does not exist" , addMem bers[ i ] )
}
addMembers [ i ] = addMem
}
if v , ok := newEntry [ ATTR_OBJECTCLASS ] ; ok && len ( v ) == 0 {
return ldap . LDAPResultInsufficientAccessRights , fmt . Errorf (
"Cannot remove all objectclass values" )
for k , v := range entry {
if strings . EqualFold ( k , ATTR_OBJECTCLASS ) && len ( v ) == 0 {
return ldap . LDAPResultInsufficientAccessRights , fmt . Errorf (
"Cannot remove all objectclass values" )
}
}
// Now, the modification has been processed and accepted and we want to commit it
n ewE ntry[ ATTR_MODIFIERSNAME ] = [ ] string { state . login . user }
n ewE ntry[ ATTR_MODIFYTIMESTAMP ] = [ ] string { genTimestamp ( ) }
entry[ ATTR_MODIFIERSNAME ] = [ ] string { state . login . user }
entry[ ATTR_MODIFYTIMESTAMP ] = [ ] string { genTimestamp ( ) }
// Save the edited values
err = server . addElements ( dn , n ewE ntry)
err = server . addElements ( dn , entry)
if err != nil {
return ldap . LDAPResultOperationsError , err
}