- commit
- f60c265
- parent
- f1dc8fc
- author
- Eric Bower
- date
- 2025-12-26 10:53:17 -0500 EST
refactor: use `resolveTopic` to determine topic name
2 files changed,
+158,
-146
+80,
-146
1@@ -9,7 +9,6 @@ import (
2 "log/slog"
3 "slices"
4 "strings"
5- "text/tabwriter"
6 "time"
7
8 "github.com/antoniomika/syncmap"
9@@ -321,42 +320,46 @@ func (handler *CliHandler) pub(cmd *CliCmd, topic string, clientID string) error
10 topic = uuid.NewString()
11 }
12
13- var withoutUser string
14- var name string
15 msgFlag := ""
16-
17- if cmd.isAdmin && strings.HasPrefix(topic, "/") {
18- name = strings.TrimPrefix(topic, "/")
19- } else {
20- name = toTopic(cmd.userName, topic)
21- if *public {
22- name = toPublicTopic(topic)
23- msgFlag = "-p "
24- withoutUser = name
25- } else {
26- withoutUser = topic
27- }
28+ if *public {
29+ msgFlag = "-p "
30 }
31
32+ // Initial resolution to get the topic name for access storage
33+ initialResult := resolveTopic(TopicResolveInput{
34+ UserName: cmd.userName,
35+ Topic: topic,
36+ IsAdmin: cmd.isAdmin,
37+ IsPublic: *public,
38+ })
39+ name := initialResult.Name
40+
41 var accessListCreator bool
42 _, loaded := handler.Access.LoadOrStore(name, accessList)
43 if !loaded {
44 defer func() {
45 handler.Access.Delete(name)
46 }()
47-
48 accessListCreator = true
49 }
50
51- if accessList, ok := handler.Access.Load(withoutUser); ok && len(accessList) > 0 && !cmd.isAdmin {
52- if checkAccess(accessList, cmd.userName, cmd.sesh) || accessListCreator {
53- name = withoutUser
54- } else if !*public {
55- name = toTopic(cmd.userName, withoutUser)
56- } else {
57- topic = uuid.NewString()
58- name = toPublicTopic(topic)
59- }
60+ // Check for existing access list and resolve final topic name
61+ existingAccessList, hasExistingAccess := handler.Access.Load(initialResult.WithoutUser)
62+ result := resolveTopic(TopicResolveInput{
63+ UserName: cmd.userName,
64+ Topic: topic,
65+ IsAdmin: cmd.isAdmin,
66+ IsPublic: *public,
67+ ExistingAccessList: existingAccessList,
68+ HasExistingAccess: hasExistingAccess,
69+ IsAccessCreator: accessListCreator,
70+ HasUserAccess: checkAccess(existingAccessList, cmd.userName, cmd.sesh),
71+ })
72+ name = result.Name
73+
74+ if result.GenerateNewTopic {
75+ topic = uuid.NewString()
76+ name = toPublicTopic(topic)
77 }
78
79 if !*clean {
80@@ -524,20 +527,14 @@ func (handler *CliHandler) sub(cmd *CliCmd, topic string, clientID string) error
81 accessList = parseArgList(*access)
82 }
83
84- var withoutUser string
85- var name string
86-
87- if cmd.isAdmin && strings.HasPrefix(topic, "/") {
88- name = strings.TrimPrefix(topic, "/")
89- } else {
90- name = toTopic(cmd.userName, topic)
91- if *public {
92- name = toPublicTopic(topic)
93- withoutUser = name
94- } else {
95- withoutUser = topic
96- }
97- }
98+ // Initial resolution to get the topic name for access storage
99+ initialResult := resolveTopic(TopicResolveInput{
100+ UserName: cmd.userName,
101+ Topic: topic,
102+ IsAdmin: cmd.isAdmin,
103+ IsPublic: *public,
104+ })
105+ name := initialResult.Name
106
107 var accessListCreator bool
108
109@@ -549,14 +546,22 @@ func (handler *CliHandler) sub(cmd *CliCmd, topic string, clientID string) error
110 accessListCreator = true
111 }
112
113- if accessList, ok := handler.Access.Load(withoutUser); ok && len(accessList) > 0 && !cmd.isAdmin {
114- if checkAccess(accessList, cmd.userName, cmd.sesh) || accessListCreator {
115- name = withoutUser
116- } else if !*public {
117- name = toTopic(cmd.userName, withoutUser)
118- } else {
119- return fmt.Errorf("access denied")
120- }
121+ // Check for existing access list and resolve final topic name
122+ existingAccessList, hasExistingAccess := handler.Access.Load(initialResult.WithoutUser)
123+ result := resolveTopic(TopicResolveInput{
124+ UserName: cmd.userName,
125+ Topic: topic,
126+ IsAdmin: cmd.isAdmin,
127+ IsPublic: *public,
128+ ExistingAccessList: existingAccessList,
129+ HasExistingAccess: hasExistingAccess,
130+ IsAccessCreator: accessListCreator,
131+ HasUserAccess: checkAccess(existingAccessList, cmd.userName, cmd.sesh),
132+ })
133+ name = result.Name
134+
135+ if result.AccessDenied {
136+ return fmt.Errorf("access denied")
137 }
138
139 err := handler.PubSub.Sub(
140@@ -612,23 +617,20 @@ func (handler *CliHandler) pipe(cmd *CliCmd, topic string, clientID string) erro
141 topic = uuid.NewString()
142 }
143
144- var withoutUser string
145- var name string
146 flagMsg := ""
147-
148- if cmd.isAdmin && strings.HasPrefix(topic, "/") {
149- name = strings.TrimPrefix(topic, "/")
150- } else {
151- name = toTopic(cmd.userName, topic)
152- if *public {
153- name = toPublicTopic(topic)
154- flagMsg = "-p "
155- withoutUser = name
156- } else {
157- withoutUser = topic
158- }
159+ if *public {
160+ flagMsg = "-p "
161 }
162
163+ // Initial resolution to get the topic name for access storage
164+ initialResult := resolveTopic(TopicResolveInput{
165+ UserName: cmd.userName,
166+ Topic: topic,
167+ IsAdmin: cmd.isAdmin,
168+ IsPublic: *public,
169+ })
170+ name := initialResult.Name
171+
172 var accessListCreator bool
173
174 _, loaded := handler.Access.LoadOrStore(name, accessList)
175@@ -639,15 +641,23 @@ func (handler *CliHandler) pipe(cmd *CliCmd, topic string, clientID string) erro
176 accessListCreator = true
177 }
178
179- if accessList, ok := handler.Access.Load(withoutUser); ok && len(accessList) > 0 && !cmd.isAdmin {
180- if checkAccess(accessList, cmd.userName, cmd.sesh) || accessListCreator {
181- name = withoutUser
182- } else if !*public {
183- name = toTopic(cmd.userName, withoutUser)
184- } else {
185- topic = uuid.NewString()
186- name = toPublicTopic(topic)
187- }
188+ // Check for existing access list and resolve final topic name
189+ existingAccessList, hasExistingAccess := handler.Access.Load(initialResult.WithoutUser)
190+ result := resolveTopic(TopicResolveInput{
191+ UserName: cmd.userName,
192+ Topic: topic,
193+ IsAdmin: cmd.isAdmin,
194+ IsPublic: *public,
195+ ExistingAccessList: existingAccessList,
196+ HasExistingAccess: hasExistingAccess,
197+ IsAccessCreator: accessListCreator,
198+ HasUserAccess: checkAccess(existingAccessList, cmd.userName, cmd.sesh),
199+ })
200+ name = result.Name
201+
202+ if result.GenerateNewTopic {
203+ topic = uuid.NewString()
204+ name = toPublicTopic(topic)
205 }
206
207 if isCreator && !*clean {
208@@ -740,82 +750,6 @@ func flagCheck(cmd *flag.FlagSet, posArg string, cmdArgs []string) bool {
209 return true
210 }
211
212-func NewTabWriter(out io.Writer) *tabwriter.Writer {
213- return tabwriter.NewWriter(out, 0, 0, 1, ' ', tabwriter.TabIndent)
214-}
215-
216-// scope topic to user by prefixing name.
217-func toTopic(userName, topic string) string {
218- if strings.HasPrefix(topic, userName+"/") {
219- return topic
220- }
221- return fmt.Sprintf("%s/%s", userName, topic)
222-}
223-
224-func toPublicTopic(topic string) string {
225- if strings.HasPrefix(topic, "public/") {
226- return topic
227- }
228- return fmt.Sprintf("public/%s", topic)
229-}
230-
231-// TopicResolveInput contains all inputs needed for topic resolution.
232-type TopicResolveInput struct {
233- UserName string
234- Topic string
235- IsAdmin bool
236- IsPublic bool
237- AccessList []string
238- ExistingAccessList []string
239- HasExistingAccess bool
240- IsAccessCreator bool
241- HasUserAccess bool
242-}
243-
244-// TopicResolveOutput contains the resolved topic name and any error.
245-type TopicResolveOutput struct {
246- Name string
247- WithoutUser string
248- AccessDenied bool
249- GenerateNewTopic bool
250-}
251-
252-// resolveTopic determines the final topic name based on user, flags, and access control.
253-func resolveTopic(input TopicResolveInput) TopicResolveOutput {
254- var name string
255- var withoutUser string
256-
257- if input.IsAdmin && strings.HasPrefix(input.Topic, "/") {
258- name = strings.TrimPrefix(input.Topic, "/")
259- return TopicResolveOutput{Name: name, WithoutUser: withoutUser}
260- }
261-
262- name = toTopic(input.UserName, input.Topic)
263- if input.IsPublic {
264- name = toPublicTopic(input.Topic)
265- withoutUser = name
266- } else {
267- withoutUser = input.Topic
268- }
269-
270- if input.HasExistingAccess && len(input.ExistingAccessList) > 0 && !input.IsAdmin {
271- if input.HasUserAccess || input.IsAccessCreator {
272- name = withoutUser
273- } else if !input.IsPublic {
274- name = toTopic(input.UserName, withoutUser)
275- } else {
276- return TopicResolveOutput{
277- Name: name,
278- WithoutUser: withoutUser,
279- AccessDenied: true,
280- GenerateNewTopic: true,
281- }
282- }
283- }
284-
285- return TopicResolveOutput{Name: name, WithoutUser: withoutUser}
286-}
287-
288 func clientInfo(clients []*psub.Client, isAdmin bool, clientType string) string {
289 if len(clients) == 0 {
290 return ""
+78,
-0
1@@ -0,0 +1,78 @@
2+package pipe
3+
4+import (
5+ "fmt"
6+ "strings"
7+)
8+
9+// toTopic scopes a topic to user by prefixing name.
10+func toTopic(userName, topic string) string {
11+ if strings.HasPrefix(topic, userName+"/") {
12+ return topic
13+ }
14+ return fmt.Sprintf("%s/%s", userName, topic)
15+}
16+
17+func toPublicTopic(topic string) string {
18+ if strings.HasPrefix(topic, "public/") {
19+ return topic
20+ }
21+ return fmt.Sprintf("public/%s", topic)
22+}
23+
24+// TopicResolveInput contains all inputs needed for topic resolution.
25+type TopicResolveInput struct {
26+ UserName string
27+ Topic string
28+ IsAdmin bool
29+ IsPublic bool
30+ AccessList []string
31+ ExistingAccessList []string
32+ HasExistingAccess bool
33+ IsAccessCreator bool
34+ HasUserAccess bool
35+}
36+
37+// TopicResolveOutput contains the resolved topic name and any error.
38+type TopicResolveOutput struct {
39+ Name string
40+ WithoutUser string
41+ AccessDenied bool
42+ GenerateNewTopic bool
43+}
44+
45+// resolveTopic determines the final topic name based on user, flags, and access control.
46+func resolveTopic(input TopicResolveInput) TopicResolveOutput {
47+ var name string
48+ var withoutUser string
49+
50+ if input.IsAdmin && strings.HasPrefix(input.Topic, "/") {
51+ name = strings.TrimPrefix(input.Topic, "/")
52+ return TopicResolveOutput{Name: name, WithoutUser: withoutUser}
53+ }
54+
55+ name = toTopic(input.UserName, input.Topic)
56+ if input.IsPublic {
57+ name = toPublicTopic(input.Topic)
58+ withoutUser = name
59+ } else {
60+ withoutUser = input.Topic
61+ }
62+
63+ if input.HasExistingAccess && len(input.ExistingAccessList) > 0 && !input.IsAdmin {
64+ if input.HasUserAccess || input.IsAccessCreator {
65+ name = withoutUser
66+ } else if !input.IsPublic {
67+ name = toTopic(input.UserName, withoutUser)
68+ } else {
69+ return TopicResolveOutput{
70+ Name: name,
71+ WithoutUser: withoutUser,
72+ AccessDenied: true,
73+ GenerateNewTopic: true,
74+ }
75+ }
76+ }
77+
78+ return TopicResolveOutput{Name: name, WithoutUser: withoutUser}
79+}