...

Source file src/github.com/sigstore/rekor/pkg/api/index.go

Documentation: github.com/sigstore/rekor/pkg/api

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    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  	// default to "or" if no operator is specified
    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  		// This must be a valid hash
    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  // Uniq is a collection of unique elements.
   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  // Intersect returns the intersection of two collections.
   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  // Union returns the union of two collections.
   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  // Collection is a collection of sets.
   182  //
   183  // its resulting values is a union or intersection of all the sets, depending on the operator.
   184  type Collection struct {
   185  	subsets  []Uniq
   186  	operator string
   187  }
   188  
   189  // NewCollection creates a new collection.
   190  func NewCollection(operator string) *Collection {
   191  	return &Collection{
   192  		subsets:  []Uniq{},
   193  		operator: operator,
   194  	}
   195  }
   196  
   197  // Add adds the elements into a new subset in the collection.
   198  func (u *Collection) Add(elements []string) {
   199  	subset := Uniq{}
   200  	subset.Add(elements...)
   201  	u.subsets = append(u.subsets, subset)
   202  }
   203  
   204  // Values flattens the subsets using the operator, and returns the collection as a slice of strings.
   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