...
1
15 package auth
16
17 import (
18 "context"
19 "sort"
20 "strings"
21 )
22
23
24
25 const (
26
27
28 ActionPull = "pull"
29
30
31
32 ActionPush = "push"
33
34
35
36 ActionDelete = "delete"
37 )
38
39
40 const ScopeRegistryCatalog = "registry:catalog:*"
41
42
43
44 func ScopeRepository(repository string, actions ...string) string {
45 actions = cleanActions(actions)
46 if repository == "" || len(actions) == 0 {
47 return ""
48 }
49 return strings.Join([]string{
50 "repository",
51 repository,
52 strings.Join(actions, ","),
53 }, ":")
54 }
55
56
57 type scopesContextKey struct{}
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 func WithScopes(ctx context.Context, scopes ...string) context.Context {
75 scopes = CleanScopes(scopes)
76 return context.WithValue(ctx, scopesContextKey{}, scopes)
77 }
78
79
80
81
82 func AppendScopes(ctx context.Context, scopes ...string) context.Context {
83 if len(scopes) == 0 {
84 return ctx
85 }
86 return WithScopes(ctx, append(GetScopes(ctx), scopes...)...)
87 }
88
89
90 func GetScopes(ctx context.Context) []string {
91 if scopes, ok := ctx.Value(scopesContextKey{}).([]string); ok {
92 return append([]string(nil), scopes...)
93 }
94 return nil
95 }
96
97
98
99
100
101
102
103 func CleanScopes(scopes []string) []string {
104
105 switch len(scopes) {
106 case 0:
107 return nil
108 case 1:
109 scope := scopes[0]
110 i := strings.LastIndex(scope, ":")
111 if i == -1 {
112 return []string{scope}
113 }
114 actionList := strings.Split(scope[i+1:], ",")
115 actionList = cleanActions(actionList)
116 if len(actionList) == 0 {
117 return nil
118 }
119 actions := strings.Join(actionList, ",")
120 scope = scope[:i+1] + actions
121 return []string{scope}
122 }
123
124
125 var result []string
126
127
128 resourceTypes := make(map[string]map[string]map[string]struct{})
129 for _, scope := range scopes {
130
131 i := strings.Index(scope, ":")
132 if i == -1 {
133 result = append(result, scope)
134 continue
135 }
136 resourceType := scope[:i]
137
138
139 rest := scope[i+1:]
140 i = strings.LastIndex(rest, ":")
141 if i == -1 {
142 result = append(result, scope)
143 continue
144 }
145 resourceName := rest[:i]
146 actions := rest[i+1:]
147 if actions == "" {
148
149 continue
150 }
151
152
153 namedActions := resourceTypes[resourceType]
154 if namedActions == nil {
155 namedActions = make(map[string]map[string]struct{})
156 resourceTypes[resourceType] = namedActions
157 }
158 actionSet := namedActions[resourceName]
159 if actionSet == nil {
160 actionSet = make(map[string]struct{})
161 namedActions[resourceName] = actionSet
162 }
163 for _, action := range strings.Split(actions, ",") {
164 if action != "" {
165 actionSet[action] = struct{}{}
166 }
167 }
168 }
169
170
171 for resourceType, namedActions := range resourceTypes {
172 for resourceName, actionSet := range namedActions {
173 if len(actionSet) == 0 {
174 continue
175 }
176 var actions []string
177 for action := range actionSet {
178 if action == "*" {
179 actions = []string{"*"}
180 break
181 }
182 actions = append(actions, action)
183 }
184 sort.Strings(actions)
185 scope := resourceType + ":" + resourceName + ":" + strings.Join(actions, ",")
186 result = append(result, scope)
187 }
188 }
189
190
191 sort.Strings(result)
192 return result
193 }
194
195
196
197 func cleanActions(actions []string) []string {
198
199 switch len(actions) {
200 case 0:
201 return nil
202 case 1:
203 if actions[0] == "" {
204 return nil
205 }
206 return actions
207 }
208
209
210 sort.Strings(actions)
211 n := 0
212 for i := 0; i < len(actions); i++ {
213 if actions[i] == "*" {
214 return []string{"*"}
215 }
216 if actions[i] != actions[n] {
217 n++
218 if n != i {
219 actions[n] = actions[i]
220 }
221 }
222 }
223 n++
224 if actions[0] == "" {
225 if n == 1 {
226 return nil
227 }
228 return actions[1:n]
229 }
230 return actions[:n]
231 }
232
View as plain text