-
Notifications
You must be signed in to change notification settings - Fork 321
/
validate.go
198 lines (183 loc) · 5.46 KB
/
validate.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
package config
import (
"fmt"
"github.com/baidu/openedge/openedge-hub/common"
"github.com/baidu/openedge/openedge-hub/utils"
"github.com/deckarep/golang-set"
)
// principalsValidate validate principals config is valid or not
func principalsValidate(v interface{}, param string) error {
principals := v.([]Principal)
err := userValidate(principals)
if err != nil {
return err
}
for _, principal := range principals {
for _, permission := range principal.Permissions {
for _, permit := range permission.Permits {
if !common.SubTopicValidate(permit) {
return fmt.Errorf("%s topic(%s) invalid", permission.Action, permit)
}
}
}
}
return nil
}
// userValidate validate username duplicate or not
func userValidate(principals []Principal) error {
userMap := make(map[string]struct{})
for _, principal := range principals {
if _, ok := userMap[principal.Username]; ok {
return fmt.Errorf("username (%s) duplicate", principal.Username)
}
userMap[principal.Username] = struct{}{}
}
return nil
}
// subscriptionsValidate check the subscriptions config is valid or not
func subscriptionsValidate(v interface{}, param string) error {
subscriptions := v.([]Subscription)
for _, s := range subscriptions {
if !common.SubTopicValidate(s.Source.Topic) {
return fmt.Errorf("[%+v] source topic invalid", s.Source)
}
if !common.PubTopicValidate(s.Target.Topic) {
return fmt.Errorf("[%+v] target topic invalid", s.Target)
}
}
// duplicate source and target config validate
_, edges := getVertexEdges(subscriptions)
if dupSubscriptionValidate(edges) {
return fmt.Errorf("duplicate source and target config")
}
// cycle found in source and target config
return cycleFound(edges)
}
// cycleFound is used for finding cycle
func cycleFound(edges [][2]string) error {
normalEdges, wildcardEdges := classifyEdges(edges)
// normal source && target config cycle detect
if !canFinish(normalEdges) {
return fmt.Errorf("found cycle in source and target config")
}
// wildcard source && target config cycle detect
targetVertexs := getTargetVertexs(edges)
for _, e := range wildcardEdges {
for _, v := range targetVertexs {
if common.TopicIsMatch(v, e[0]) {
e[0] = v
normalEdges = append(normalEdges, e)
if !canFinish(normalEdges) {
return fmt.Errorf("found cycle in source and target config")
}
}
}
}
return nil
}
// canFinish detect a directed graph has cycle or not
// vertexs store graph vertex info, edges store graph edge info
func canFinish(edges [][2]string) bool {
if len(edges[0]) == 0 || len(edges) == 0 {
return true
}
vertexs := edges2Vertexs(edges)
graph := make(map[string][]string)
for i := range edges {
graph[edges[i][1]] = append(graph[edges[i][1]], edges[i][0])
}
path := make([]bool, len(vertexs))
visited := make([]bool, len(vertexs))
for i := range vertexs {
if visited[i] {
continue
}
if hasCycle(vertexs, vertexs[i], graph, path, visited) {
return false
}
}
return true
}
// hasCycle check the directed graph has cycle or not
// graph store some vertex which is associated with the given vertex
// visited represents an vertex visit-status, if true, visited; else none visited
func hasCycle(vertexs []string, start string, graph map[string][]string, path []bool, visited []bool) bool {
for i := range graph[start] {
if visited[getPosition(graph[start][i], vertexs)] {
continue
}
if path[getPosition(graph[start][i], vertexs)] {
return true
}
path[getPosition(graph[start][i], vertexs)] = true
if hasCycle(vertexs, graph[start][i], graph, path, visited) {
return true
}
path[getPosition(graph[start][i], vertexs)] = false
}
visited[getPosition(start, vertexs)] = true
return false
}
// getPosition return the given data's index of the given vertexs
func getPosition(data string, vertexs []string) int {
for pos := 0; pos < len(vertexs); pos++ {
if vertexs[pos] == data {
return pos
}
}
return -1
}
// getVertexEdges generate vertexs && edges
func getVertexEdges(subscriptions []Subscription) ([]string, [][2]string) {
vertexs := make(map[string]struct{})
edges := make([][2]string, 0)
for _, s := range subscriptions {
st := s.Source.Topic
tt := s.Target.Topic
vertexs[st] = struct{}{}
vertexs[tt] = struct{}{}
edges = append(edges, [2]string{st, tt})
}
return utils.GetKeys(vertexs), edges
}
// classifyEdges generate normalEges && wildcardEdges
func classifyEdges(edges [][2]string) ([][2]string, [][2]string) {
normalEdges := make([][2]string, 0)
wildcardEdges := make([][2]string, 0)
for _, e := range edges {
if common.ContainsWildcard(e[0]) {
wildcardEdges = append(wildcardEdges, e)
} else {
normalEdges = append(normalEdges, e)
}
}
return normalEdges, wildcardEdges
}
// edges2Vertexs generate vertexs from given edges
func edges2Vertexs(edges [][2]string) []string {
vertexs := make(map[string]struct{})
for _, e := range edges {
vertexs[e[0]] = struct{}{}
vertexs[e[1]] = struct{}{}
}
return utils.GetKeys(vertexs)
}
// getTargetVertexs generate target vertexs from given edges
func getTargetVertexs(edges [][2]string) []string {
vertexs := make(map[string]struct{})
for _, e := range edges {
vertexs[e[1]] = struct{}{}
}
return utils.GetKeys(vertexs)
}
// dupSubscriptionValidate check subscription config has duplicate config or not
func dupSubscriptionValidate(edges [][2]string) bool {
_edges := mapset.NewSet()
for _, element := range edges {
_edges.Add(element)
}
if _edges.Cardinality() != len(edges) {
return true
}
return false
}