1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package api
17
18 import (
19 "context"
20 "crypto/sha256"
21 "encoding/hex"
22 "fmt"
23 "net/http"
24 "strings"
25
26 "github.com/go-openapi/runtime/middleware"
27 "github.com/go-openapi/swag"
28
29 "github.com/sigstore/rekor/pkg/generated/models"
30 "github.com/sigstore/rekor/pkg/generated/restapi/operations/index"
31 "github.com/sigstore/rekor/pkg/pki"
32 "github.com/sigstore/rekor/pkg/util"
33 )
34
35 func SearchIndexHandler(params index.SearchIndexParams) middleware.Responder {
36 httpReqCtx := params.HTTPRequest.Context()
37
38 queryOperator := params.Query.Operator
39
40 if params.Query.Operator == "" {
41 queryOperator = "or"
42 }
43 var result = NewCollection(queryOperator)
44
45 var lookupKeys []string
46
47 if params.Query.Hash != "" {
48
49 sha := strings.ToLower(util.PrefixSHA(params.Query.Hash))
50 if queryOperator == "or" {
51 lookupKeys = append(lookupKeys, sha)
52 } else {
53 resultUUIDs, err := indexStorageClient.LookupIndices(httpReqCtx, []string{sha})
54 if err != nil {
55 return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("index storage error: %w", err), indexStorageUnexpectedResult)
56 }
57 result.Add(resultUUIDs)
58 }
59 }
60 if params.Query.PublicKey != nil {
61 af, err := pki.NewArtifactFactory(pki.Format(swag.StringValue(params.Query.PublicKey.Format)))
62 if err != nil {
63 return handleRekorAPIError(params, http.StatusBadRequest, err, unsupportedPKIFormat)
64 }
65 keyReader, err := util.FileOrURLReadCloser(httpReqCtx, params.Query.PublicKey.URL.String(), params.Query.PublicKey.Content)
66 if err != nil {
67 return handleRekorAPIError(params, http.StatusBadRequest, err, malformedPublicKey)
68 }
69 defer keyReader.Close()
70
71 key, err := af.NewPublicKey(keyReader)
72 if err != nil {
73 return handleRekorAPIError(params, http.StatusBadRequest, err, malformedPublicKey)
74 }
75 canonicalKey, err := key.CanonicalValue()
76 if err != nil {
77 return handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateCanonicalKey)
78 }
79
80 keyHash := sha256.Sum256(canonicalKey)
81 keyHashStr := strings.ToLower(hex.EncodeToString(keyHash[:]))
82 if queryOperator == "or" {
83 lookupKeys = append(lookupKeys, keyHashStr)
84 } else {
85 resultUUIDs, err := indexStorageClient.LookupIndices(httpReqCtx, []string{keyHashStr})
86 if err != nil {
87 return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("index storage error: %w", err), indexStorageUnexpectedResult)
88 }
89 result.Add(resultUUIDs)
90 }
91 }
92 if params.Query.Email != "" {
93 emailStr := strings.ToLower(params.Query.Email.String())
94 if queryOperator == "or" {
95 lookupKeys = append(lookupKeys, emailStr)
96 } else {
97 resultUUIDs, err := indexStorageClient.LookupIndices(httpReqCtx, []string{emailStr})
98 if err != nil {
99 return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("index storage error: %w", err), indexStorageUnexpectedResult)
100 }
101 result.Add(resultUUIDs)
102 }
103 }
104 if len(lookupKeys) > 0 {
105 resultUUIDs, err := indexStorageClient.LookupIndices(httpReqCtx, lookupKeys)
106 if err != nil {
107 return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("index storage error: %w", err), indexStorageUnexpectedResult)
108 }
109 result.Add(resultUUIDs)
110 }
111
112 return index.NewSearchIndexOK().WithPayload(result.Values())
113 }
114
115 func SearchIndexNotImplementedHandler(_ index.SearchIndexParams) middleware.Responder {
116 err := models.Error{
117 Code: http.StatusNotImplemented,
118 Message: "Search Index API not enabled in this Rekor instance",
119 }
120
121 return index.NewSearchIndexDefault(http.StatusNotImplemented).WithPayload(&err)
122
123 }
124
125 func addToIndex(ctx context.Context, keys []string, value string) error {
126 err := indexStorageClient.WriteIndex(ctx, keys, value)
127 if err != nil {
128 return fmt.Errorf("redis client: %w", err)
129 }
130 return nil
131 }
132
133 func storeAttestation(ctx context.Context, uuid string, attestation []byte) error {
134 return attestationStorageClient.StoreAttestation(ctx, uuid, attestation)
135 }
136
137
138 type Uniq map[string]struct{}
139
140 func NewUniq() Uniq {
141 return make(Uniq)
142 }
143
144 func (u Uniq) Add(elements ...string) {
145 for _, e := range elements {
146 u[e] = struct{}{}
147 }
148 }
149
150 func (u Uniq) Values() []string {
151 var result []string
152 for k := range u {
153 result = append(result, k)
154 }
155 return result
156 }
157
158
159 func (u Uniq) Intersect(other Uniq) Uniq {
160 result := make(Uniq)
161 for k := range u {
162 if _, ok := other[k]; ok {
163 result[k] = struct{}{}
164 }
165 }
166 return result
167 }
168
169
170 func (u Uniq) Union(other Uniq) Uniq {
171 result := make(Uniq)
172 for k := range u {
173 result[k] = struct{}{}
174 }
175 for k := range other {
176 result[k] = struct{}{}
177 }
178 return result
179 }
180
181
182
183
184 type Collection struct {
185 subsets []Uniq
186 operator string
187 }
188
189
190 func NewCollection(operator string) *Collection {
191 return &Collection{
192 subsets: []Uniq{},
193 operator: operator,
194 }
195 }
196
197
198 func (u *Collection) Add(elements []string) {
199 subset := Uniq{}
200 subset.Add(elements...)
201 u.subsets = append(u.subsets, subset)
202 }
203
204
205 func (u *Collection) Values() []string {
206 if len(u.subsets) == 0 {
207 return []string{}
208 }
209 subset := u.subsets[0]
210 for i := 1; i < len(u.subsets); i++ {
211 if strings.EqualFold(u.operator, "and") {
212 subset = subset.Intersect(u.subsets[i])
213 } else {
214 subset = subset.Union(u.subsets[i])
215 }
216 }
217 return subset.Values()
218 }
219
View as plain text