-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathids.go
122 lines (112 loc) · 2.76 KB
/
ids.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package pkg
import (
"fmt"
"strconv"
"strings"
"github.com/go-ldap/ldap/v3"
log "github.com/sirupsen/logrus"
)
type highestIDRequest struct {
attribute string
min int
entryBaseDN string
entryFilter string
entryAttribute string
}
// GetHighestUID gets the highest UID
func (m *LDAPManager) GetHighestUID() (int, error) {
req := highestIDRequest{
attribute: m.AccountAttribute,
min: MinUID,
entryBaseDN: m.UserGroupDN,
entryFilter: fmt.Sprintf("(%s=*)", m.AccountAttribute),
entryAttribute: "uidNumber",
}
return m.getHighestID(&req)
}
// GetHighestGID gets the highest GID
func (m *LDAPManager) GetHighestGID() (int, error) {
req := highestIDRequest{
attribute: m.GroupAttribute,
min: MinGID + 1, // we reserve MinGID for the users group
entryBaseDN: m.GroupsDN,
entryFilter: "(objectClass=posixGroup)",
entryAttribute: "gidNumber",
}
return m.getHighestID(&req)
}
func (m *LDAPManager) getHighestID(req *highestIDRequest) (int, error) {
// check for cached lastUID / lastGID value first
attribute := strings.ToUpper(req.attribute)
filter := fmt.Sprintf(
"(&(objectClass=device)(cn=last%s))",
attribute,
)
conn, err := m.Pool.Get()
if err != nil {
return 0, err
}
defer conn.Close()
result, err := conn.Search(ldap.NewSearchRequest(
m.BaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
filter,
[]string{"serialNumber"},
[]ldap.Control{},
))
if err != nil {
return 0, err
}
if len(result.Entries) > 0 {
serial := result.Entries[0].GetAttributeValue("serialNumber")
fetchedID, err := strconv.Atoi(serial)
if err == nil && fetchedID >= req.min {
return fetchedID, nil
}
}
// cache miss requires traversing all entries
result, err = conn.Search(ldap.NewSearchRequest(
req.entryBaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
req.entryFilter,
[]string{req.entryAttribute},
[]ldap.Control{},
))
if err != nil {
return req.min, err
}
highestID := req.min
for _, entry := range result.Entries {
if id := entry.GetAttributeValue(req.entryAttribute); id != "" {
if id, err := strconv.Atoi(id); err == nil {
if id > highestID {
highestID = id
}
}
}
}
return highestID, nil
}
// updateLastID updates the id cache holding the last ID
func (m *LDAPManager) updateLastID(cn string, lastID int) error {
req := ldap.NewModifyRequest(
fmt.Sprintf("cn=%s,%s", cn, m.BaseDN),
[]ldap.Control{},
)
req.Replace("serialNumber", []string{
strconv.Itoa(lastID),
})
log.Debug(PrettyPrint(req))
conn, err := m.Pool.Get()
if err != nil {
return err
}
defer conn.Close()
if err := conn.Modify(req); err != nil {
return fmt.Errorf(
"failed to update cn=%s: %v",
cn, err,
)
}
return nil
}