-
Notifications
You must be signed in to change notification settings - Fork 52
/
acls.go
243 lines (217 loc) · 7.12 KB
/
acls.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package runtime
import (
"fmt"
"regexp"
"strconv"
"strings"
native_errors "github.com/haproxytech/client-native/v6/errors"
"github.com/haproxytech/client-native/v6/models"
)
// ShowACLS returns Acl files description from runtime
func (s *SingleRuntime) ShowACLS() (models.ACLFiles, error) {
response, err := s.ExecuteWithResponse("show acl")
if err != nil {
return nil, fmt.Errorf("%s %w", err.Error(), native_errors.ErrNotFound)
}
return s.parseACLS(response), nil
}
// parseACLS parses output from `show acl` command and return array of acl files
// First line in output represents format and is ignored
// Sample output format:
// # id (file) description
// -0 (/etc/acl/blocklist.txt) pattern loaded from file '/etc/acl/blocklist.txt' used by acl at file '/usr/local/etc/haproxy/haproxy.cfg' line 59
// -1 () acl 'src' file '/usr/local/etc/haproxy/haproxy.cfg' line 59
func (s *SingleRuntime) parseACLS(output string) models.ACLFiles {
if output == "" {
return nil
}
acls := models.ACLFiles{}
lines := strings.Split(strings.TrimSpace(output), "\n")
for _, line := range lines {
m := s.parseACL(line)
if m != nil {
acls = append(acls, m)
}
}
return acls
}
// parseACL parses one line from ACL files array and return it structured
func (s *SingleRuntime) parseACL(line string) *models.ACLFile {
if line == "" || strings.HasPrefix(strings.TrimSpace(line), "# id") {
return nil
}
parts := strings.Fields(line)
if len(parts) < 3 {
return nil
}
m := &models.ACLFile{
ID: parts[0],
StorageName: findStorageName(parts[1], line),
Description: strings.Join(parts[2:], " "),
}
return m
}
// findStorageName checks if acl name exists and extracts it
func findStorageName(name, line string) string {
name = strings.TrimSuffix(strings.TrimPrefix(name, "("), ")")
if name == "" {
re := regexp.MustCompile(`acl\s'(.*)'\sfile`)
matches := re.FindStringSubmatch(line)
if matches != nil {
name = matches[1]
}
}
return name
}
// GetACL returns one structured runtime Acl file
func (s *SingleRuntime) GetACL(storageName string) (*models.ACLFile, error) {
if storageName == "" {
return nil, fmt.Errorf("%s %w", "Argument nameOrFile empty", native_errors.ErrGeneral)
}
acls, err := s.ShowACLS()
if err != nil {
return nil, err
}
for _, m := range acls {
if m.StorageName == storageName || storageName == "#"+m.ID {
return m, nil
}
}
return nil, fmt.Errorf("%s %w", storageName, native_errors.ErrNotFound)
}
// ShowACLFileEntries returns one acl runtime entries
func (s *SingleRuntime) ShowACLFileEntries(storageName string) (models.ACLFilesEntries, error) {
if storageName == "" {
return nil, fmt.Errorf("%s %w", "Argument file empty", native_errors.ErrGeneral)
}
cmd := fmt.Sprintf("show acl %s", storageName)
response, err := s.ExecuteWithResponse(cmd)
if err != nil {
return nil, fmt.Errorf("%s %w", err.Error(), native_errors.ErrNotFound)
}
return ParseACLFileEntries(response, true)
}
// ParseACLFileEntries parses array of entries in one Acl file
// One line sample entry:
// ID Value
// 0x560f3f9e8600 10.178.160.0
func ParseACLFileEntries(output string, hasID bool) (models.ACLFilesEntries, error) {
if output == "" || strings.HasPrefix(strings.TrimSpace(output), "Unknown ACL identifier.") {
return nil, native_errors.ErrNotFound
}
me := models.ACLFilesEntries{}
lines := strings.Split(strings.TrimSpace(output), "\n")
for _, line := range lines {
e := parseACLFileEntry(line, hasID)
if e != nil {
me = append(me, e)
}
}
return me, nil
}
// parseACLFileEntry parses one entry in one Acl file/runtime and returns it structured
func parseACLFileEntry(line string, hasID bool) *models.ACLFileEntry {
if line == "" || strings.HasPrefix(strings.TrimSpace(line), "#") {
return nil
}
parts := strings.Fields(line)
if len(parts) < 2 {
return nil
}
m := &models.ACLFileEntry{}
if hasID {
m.ID = parts[0] // acl entries from runtime have ID
m.Value = parts[1]
} else {
m.Value = parts[0]
}
return m
}
// AddACLFileEntry adds an entry into the Acl file
func (s *SingleRuntime) AddACLFileEntry(aclID, value string) error {
if aclID == "" || value == "" {
return fmt.Errorf("%s %w", "One or more Arguments empty", native_errors.ErrGeneral)
}
m, _ := s.GetACLFileEntry(aclID, value)
if m != nil {
return fmt.Errorf("%w", native_errors.ErrAlreadyExists)
}
cmd := fmt.Sprintf("add acl #%s %s", aclID, value)
response, err := s.ExecuteWithResponse(cmd)
if err != nil {
return fmt.Errorf("%s %w", err.Error(), native_errors.ErrGeneral)
}
if strings.Contains(response, "not") && strings.Contains(response, "valid") {
return fmt.Errorf("%s %w", strings.TrimSpace(response), native_errors.ErrGeneral)
}
return nil
}
func (s *SingleRuntime) PrepareACL(aclID string) (version string, err error) {
cmd := fmt.Sprintf("prepare acl %s", aclID)
response, err := s.ExecuteWithResponse(cmd)
if err != nil {
return "", fmt.Errorf("%s %w", err.Error(), native_errors.ErrGeneral)
}
parts := strings.Split(response, ":")
if len(parts) < 3 {
return "", fmt.Errorf("%s %w", err.Error(), native_errors.ErrGeneral)
}
version = strings.TrimSpace(parts[2])
if _, err = strconv.ParseInt(version, 10, 64); err == nil {
return version, nil
}
return "", fmt.Errorf("%s %w", err.Error(), native_errors.ErrGeneral)
}
func (s *SingleRuntime) AddACLVersioned(version, aclID, value string) error {
cmd := fmt.Sprintf("add acl @%s %s %s", version, aclID, value)
if err := s.Execute(cmd); err != nil {
return fmt.Errorf("%s %w", err.Error(), native_errors.ErrGeneral)
}
return nil
}
func (s *SingleRuntime) CommitACL(version, aclID string) error {
cmd := fmt.Sprintf("commit acl @%s %s", version, aclID)
if err := s.Execute(cmd); err != nil {
return fmt.Errorf("%s %w", err.Error(), native_errors.ErrGeneral)
}
return nil
}
// GetACLFileEntry returns one Acl runtime setting
func (s *SingleRuntime) GetACLFileEntry(aclID, value string) (*models.ACLFileEntry, error) {
cmd := fmt.Sprintf("get acl #%s %s", aclID, value)
response, err := s.ExecuteWithResponse(cmd)
if err != nil {
return nil, fmt.Errorf("%s %w", err.Error(), native_errors.ErrNotFound)
}
matched := false
m := &models.ACLFileEntry{}
parts := strings.Split(response, ",")
for _, p := range parts {
kv := strings.Split(p, "=")
switch key := strings.TrimSpace(kv[0]); {
case key == "pattern":
m.Value = strings.Trim(strings.TrimSpace(kv[1]), "\"")
case key == "match":
matched = true
}
}
if m.Value == "" || !matched {
return nil, fmt.Errorf("%s %w", value, native_errors.ErrNotFound)
}
return m, nil
}
// DeleteACLFileEntry deletes all the Acl entries from the Acl by its value
func (s *SingleRuntime) DeleteACLFileEntry(aclID, value string) error {
if aclID == "" || value == "" {
return fmt.Errorf("%s %w", "One or more Arguments empty", native_errors.ErrGeneral)
}
cmd := fmt.Sprintf("del acl #%s %s", aclID, value)
response, err := s.ExecuteWithResponse(cmd)
if err != nil {
return fmt.Errorf("%s %w", err.Error(), native_errors.ErrNotFound)
}
if strings.Contains(response, "not") && strings.Contains(response, "found") {
return fmt.Errorf("%s %w", strings.TrimSpace(response), native_errors.ErrGeneral)
}
return nil
}