aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2020-01-19 22:21:05 +0100
committerAlex Auvolat <alex@adnab.me>2020-01-19 22:21:05 +0100
commit7e4079b3d8622bc3a90e315592bbf8b3fc7fca39 (patch)
tree11fa5057b6c37bda6b0e3e9237d2e305f08fc797
parentf9cb0552be8f88531b27628f277932560ad32319 (diff)
downloadbottin-7e4079b3d8622bc3a90e315592bbf8b3fc7fca39.tar.gz
bottin-7e4079b3d8622bc3a90e315592bbf8b3fc7fca39.zip
Implement Modify
-rw-r--r--main.go191
1 files changed, 190 insertions, 1 deletions
diff --git a/main.go b/main.go
index d73ada4..d905d72 100644
--- a/main.go
+++ b/main.go
@@ -43,7 +43,7 @@ func consulToDN(pair *consul.KVPair) (string, string, []byte) {
}
dn = cpath + dn
}
- return dn, "", nil
+ panic("Consul key " + pair.Key + " does not end with attribute=something")
}
func parseValue(value []byte) ([]string, error) {
@@ -156,6 +156,7 @@ func main() {
routes.Add(gobottin.handleAdd)
routes.Compare(gobottin.handleCompare)
routes.Delete(gobottin.handleDelete)
+ routes.Modify(gobottin.handleModify)
ldapserver.Handle(routes)
// listen on 10389
@@ -686,3 +687,191 @@ func (server *Server) handleDeleteInternal(state *State, r *message.DelRequest)
return ldap.LDAPResultSuccess, nil
}
+
+func (server *Server) handleModify(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) {
+ state := s.(*State)
+ r := m.GetModifyRequest()
+
+ code, err := server.handleModifyInternal(state, &r)
+
+ res := ldap.NewResponse(code)
+ if err != nil {
+ res.SetDiagnosticMessage(err.Error())
+ }
+ w.Write(message.ModifyResponse(res))
+}
+
+func (server *Server) handleModifyInternal(state *State, r *message.ModifyRequest) (int, error) {
+ dn := string(r.Object())
+
+ _, err := server.checkSuffix(dn, false)
+ if err != nil {
+ return ldap.LDAPResultInvalidDNSyntax, err
+ }
+
+ // TODO check user for permissions to write dn
+
+ // Retrieve previous values (by the way, check object exists)
+ items, _, err := server.kv.List(dnToConsul(dn) + "/attribute=", nil)
+ if err != nil {
+ return ldap.LDAPResultOperationsError, err
+ }
+
+ if len(items) == 0 {
+ return ldap.LDAPResultNoSuchObject, fmt.Errorf("Not found: %s", dn)
+ }
+
+ prevEntry := Entry{}
+ for _, item := range items {
+ itemDN, attr, val := consulToDN(item)
+ if itemDN != dn {
+ panic("itemDN != dn in handleModifyInternal")
+ }
+ vals, err := parseValue(val)
+ if err != nil {
+ return ldap.LDAPResultOperationsError, err
+ }
+ prevEntry[attr] = vals
+ }
+
+ // Keep track of group members added/deleted
+ addMembers, delMembers := []string{}, []string{}
+
+ // Produce new entry values to be saved
+ newEntry := Entry{}
+ for _, change := range r.Changes() {
+ attr := string(change.Modification().Type_())
+ values := change.Modification().Vals()
+
+ 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 !present {
+ newEntry[attr] = append(newEntry[attr], string(val))
+ if strings.EqualFold(attr, "member") {
+ addMembers = append(addMembers, string(val))
+ }
+ }
+ }
+ } else if change.Operation() == ldap.ModifyRequestChangeOperationDelete {
+ if len(values) == 0 {
+ // Delete everything
+ newEntry[attr] = []string{}
+ if strings.EqualFold(attr, "member") {
+ delMembers = append(delMembers, prevEntry[attr]...)
+ }
+ } 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)
+ } else {
+ if strings.EqualFold(attr, "member") {
+ delMembers = append(delMembers, prevVal)
+ }
+ }
+ }
+ }
+ } else if change.Operation() == ldap.ModifyRequestChangeOperationReplace {
+ newEntry[attr] = []string{}
+ for _, newVal := range values {
+ newEntry[attr] = append(newEntry[attr], string(newVal))
+ }
+ if strings.EqualFold(attr, "member") {
+ for _, newMem := range newEntry[attr] {
+ mustAdd := true
+ for _, prevMem := range prevEntry[attr] {
+ if prevMem == newMem {
+ mustAdd = false
+ break
+ }
+ }
+ if mustAdd {
+ addMembers = append(addMembers, newMem)
+ }
+ }
+ for _, prevMem := range prevEntry[attr] {
+ mustDel := true
+ for _, newMem := range newEntry[attr] {
+ if newMem == prevMem {
+ mustDel = false
+ break
+ }
+ }
+ if mustDel {
+ delMembers = append(delMembers, prevMem)
+ }
+ }
+ }
+ }
+ }
+
+ // Check that added members actually exist
+ for _, addMem := range addMembers {
+ exists, err := server.objectExists(addMem)
+ if err != nil {
+ return ldap.LDAPResultOperationsError, err
+ }
+ if !exists {
+ return ldap.LDAPResultNoSuchObject, fmt.Errorf(
+ "Cannot add member %s, it does not exist", addMem)
+ }
+ }
+
+ // Save the edited values
+ server.addElements(dn, newEntry)
+
+ // Update memberOf for added members and deleted members
+ for _, addMem := range addMembers {
+ memberOf, err := server.getAttribute(addMem, "memberOf")
+ if err != nil {
+ return ldap.LDAPResultOperationsError, err
+ }
+ if memberOf == nil {
+ memberOf = []string{}
+ }
+ memberOf = append(memberOf, dn)
+ err = server.addElements(addMem, Entry{"memberOf": memberOf})
+ if err != nil {
+ return ldap.LDAPResultOperationsError, err
+ }
+ }
+
+ for _, delMem := range delMembers {
+ memberOf, err := server.getAttribute(delMem, "memberOf")
+ if err != nil {
+ return ldap.LDAPResultOperationsError, err
+ }
+ if memberOf == nil {
+ memberOf = []string{}
+ }
+ newMemberOf := []string{}
+ for _, g := range memberOf {
+ if g != dn {
+ newMemberOf = append(newMemberOf, g)
+ }
+ }
+
+ err = server.addElements(delMem, Entry{"memberOf": newMemberOf})
+ if err != nil {
+ return ldap.LDAPResultOperationsError, err
+ }
+ }
+
+ return ldap.LDAPResultSuccess, nil
+}