@@ -2,56 +2,54 @@ package admin
2
2
3
3
import (
4
4
"crypto/sha1"
5
+ "encoding/binary"
6
+ "encoding/hex"
7
+ "fmt"
8
+ "sort"
9
+ "strings"
5
10
"sync"
6
11
7
12
"github.com/pkg/errors"
8
- "go.step.sm/crypto/jose "
13
+ "github.com/smallstep/certificates/authority/provisioner "
9
14
)
10
15
11
- // DefaultProvisionersLimit is the default limit for listing provisioners.
12
- const DefaultProvisionersLimit = 20
16
+ // DefaultAdminLimit is the default limit for listing provisioners.
17
+ const DefaultAdminLimit = 20
13
18
14
- // DefaultProvisionersMax is the maximum limit for listing provisioners.
15
- const DefaultProvisionersMax = 100
19
+ // DefaultAdminMax is the maximum limit for listing provisioners.
20
+ const DefaultAdminMax = 100
16
21
17
- /*
18
- type uidProvisioner struct {
19
- provisioner Interface
20
- uid string
22
+ type uidAdmin struct {
23
+ admin * Admin
24
+ uid string
21
25
}
22
26
23
- type provisionerSlice []uidProvisioner
24
-
25
- func (p provisionerSlice) Len() int { return len(p) }
26
- func (p provisionerSlice) Less(i, j int) bool { return p[i].uid < p[j].uid }
27
- func (p provisionerSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
28
- */
27
+ type adminSlice []uidAdmin
29
28
30
- // loadByTokenPayload is a payload used to extract the id used to load the
31
- // provisioner.
32
- type loadByTokenPayload struct {
33
- jose.Claims
34
- AuthorizedParty string `json:"azp"` // OIDC client id
35
- TenantID string `json:"tid"` // Microsoft Azure tenant id
36
- }
29
+ func (p adminSlice ) Len () int { return len (p ) }
30
+ func (p adminSlice ) Less (i , j int ) bool { return p [i ].uid < p [j ].uid }
31
+ func (p adminSlice ) Swap (i , j int ) { p [i ], p [j ] = p [j ], p [i ] }
37
32
38
33
// Collection is a memory map of admins.
39
34
type Collection struct {
40
35
byID * sync.Map
41
36
bySubProv * sync.Map
42
37
byProv * sync.Map
38
+ sorted adminSlice
39
+ provisioners * provisioner.Collection
43
40
count int
44
41
countByProvisioner map [string ]int
45
42
}
46
43
47
44
// NewCollection initializes a collection of provisioners. The given list of
48
45
// audiences are the audiences used by the JWT provisioner.
49
- func NewCollection () * Collection {
46
+ func NewCollection (provisioners * provisioner. Collection ) * Collection {
50
47
return & Collection {
51
48
byID : new (sync.Map ),
52
49
byProv : new (sync.Map ),
53
50
bySubProv : new (sync.Map ),
54
51
countByProvisioner : map [string ]int {},
52
+ provisioners : provisioners ,
55
53
}
56
54
}
57
55
@@ -88,12 +86,18 @@ func (c *Collection) LoadByProvisioner(provName string) ([]*Admin, bool) {
88
86
// Store adds an admin to the collection and enforces the uniqueness of
89
87
// admin IDs and amdin subject <-> provisioner name combos.
90
88
func (c * Collection ) Store (adm * Admin ) error {
91
- provName := adm .ProvisionerName
89
+ p , ok := c .provisioners .Load (adm .ProvisionerID )
90
+ if ! ok {
91
+ return fmt .Errorf ("provisioner %s not found" , adm .ProvisionerID )
92
+ }
93
+ adm .ProvisionerName = p .GetName ()
94
+ adm .ProvisionerType = p .GetType ().String ()
92
95
// Store admin always in byID. ID must be unique.
93
96
if _ , loaded := c .byID .LoadOrStore (adm .ID , adm ); loaded {
94
97
return errors .New ("cannot add multiple admins with the same id" )
95
98
}
96
99
100
+ provName := adm .ProvisionerName
97
101
// Store admin alwasy in bySubProv. Subject <-> ProvisionerName must be unique.
98
102
if _ , loaded := c .bySubProv .LoadOrStore (subProvNameHash (adm .Subject , provName ), adm ); loaded {
99
103
c .byID .Delete (adm .ID )
@@ -109,6 +113,21 @@ func (c *Collection) Store(adm *Admin) error {
109
113
}
110
114
c .count ++
111
115
116
+ // Store sorted admins.
117
+ // Use the first 4 bytes (32bit) of the sum to insert the order
118
+ // Using big endian format to get the strings sorted:
119
+ // 0x00000000, 0x00000001, 0x00000002, ...
120
+ bi := make ([]byte , 4 )
121
+ _sum := sha1 .Sum ([]byte (adm .ID ))
122
+ sum := _sum [:]
123
+ binary .BigEndian .PutUint32 (bi , uint32 (c .sorted .Len ()))
124
+ sum [0 ], sum [1 ], sum [2 ], sum [3 ] = bi [0 ], bi [1 ], bi [2 ], bi [3 ]
125
+ c .sorted = append (c .sorted , uidAdmin {
126
+ admin : adm ,
127
+ uid : hex .EncodeToString (sum ),
128
+ })
129
+ sort .Sort (c .sorted )
130
+
112
131
return nil
113
132
}
114
133
@@ -125,31 +144,29 @@ func (c *Collection) CountByProvisioner(provName string) int {
125
144
return 0
126
145
}
127
146
128
- /*
129
147
// Find implements pagination on a list of sorted provisioners.
130
- func (c *Collection) Find(cursor string, limit int) (List , string) {
148
+ func (c * Collection ) Find (cursor string , limit int ) ([] * Admin , string ) {
131
149
switch {
132
150
case limit <= 0 :
133
- limit = DefaultProvisionersLimit
134
- case limit > DefaultProvisionersMax :
135
- limit = DefaultProvisionersMax
151
+ limit = DefaultAdminLimit
152
+ case limit > DefaultAdminMax :
153
+ limit = DefaultAdminMax
136
154
}
137
155
138
156
n := c .sorted .Len ()
139
157
cursor = fmt .Sprintf ("%040s" , cursor )
140
158
i := sort .Search (n , func (i int ) bool { return c .sorted [i ].uid >= cursor })
141
159
142
- slice := List {}
160
+ slice := [] * Admin {}
143
161
for ; i < n && len (slice ) < limit ; i ++ {
144
- slice = append(slice, c.sorted[i].provisioner )
162
+ slice = append (slice , c .sorted [i ].admin )
145
163
}
146
164
147
165
if i < n {
148
166
return slice , strings .TrimLeft (c .sorted [i ].uid , "0" )
149
167
}
150
168
return slice , ""
151
169
}
152
- */
153
170
154
171
func loadAdmin (m * sync.Map , key string ) (* Admin , bool ) {
155
172
a , ok := m .Load (key )
0 commit comments