Skip to content

Commit

Permalink
feat(go): add generateSecuredApiKey helper (#2797)
Browse files Browse the repository at this point in the history
  • Loading branch information
millotp authored Feb 28, 2024
1 parent 6a69064 commit 7a2d369
Show file tree
Hide file tree
Showing 13 changed files with 117 additions and 41 deletions.
5 changes: 4 additions & 1 deletion playground/go/analytics.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (

func testAnalytics(appID, apiKey string) int {
indexName := getEnvWithDefault("ANALYTICS_INDEX", "test_index")
analyticsClient := analytics.NewClient(appID, apiKey, analytics.US)
analyticsClient, err := analytics.NewClient(appID, apiKey, analytics.US)
if err != nil {
panic(err)
}

getTopFilterForAttributeResponse, err := analyticsClient.GetTopFilterForAttribute(
analyticsClient.NewApiGetTopFilterForAttributeRequest("myAttribute1,myAttribute2", indexName),
Expand Down
12 changes: 6 additions & 6 deletions playground/go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ require (
)

require (
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.15.0 // indirect
github.com/go-playground/validator/v10 v10.18.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
36 changes: 14 additions & 22 deletions playground/go/go.sum
Original file line number Diff line number Diff line change
@@ -1,38 +1,30 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
5 changes: 4 additions & 1 deletion playground/go/ingestion.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (
)

func testIngestion(appID, apiKey string) int {
ingestionClient := ingestion.NewClient(appID, apiKey, ingestion.US)
ingestionClient, err := ingestion.NewClient(appID, apiKey, ingestion.US)
if err != nil {
panic(err)
}

// another example to generate payload for a request.
createAuthenticationResponse, err := ingestionClient.CreateAuthentication(ingestionClient.NewApiCreateAuthenticationRequest(
Expand Down
7 changes: 5 additions & 2 deletions playground/go/insights.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (
)

func testInsights(appID, apiKey string) int {
insightsClient := insights.NewClient(appID, apiKey, insights.US)
insightsClient, err := insights.NewClient(appID, apiKey, insights.US)
if err != nil {
panic(err)
}

events := insights.NewInsightsEvents([]insights.EventsItems{
insights.ClickedObjectIDsAsEventsItems(insights.NewClickedObjectIDs("myEvent",
*insights.ClickedObjectIDsAsEventsItems(insights.NewClickedObjectIDs("myEvent",
insights.CLICKEVENT_CLICK,
"test_index",
[]string{"myObjectID"},
Expand Down
5 changes: 4 additions & 1 deletion playground/go/personalization.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import (
)

func testPersonalization(appID, apiKey string) int {
personalizationClient := personalization.NewClient(appID, apiKey, personalization.US)
personalizationClient, err := personalization.NewClient(appID, apiKey, personalization.US)
if err != nil {
panic(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()

Expand Down
5 changes: 4 additions & 1 deletion playground/go/query-suggestions.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import (
)

func testQuerySuggestions(appID, apiKey string) int {
suggestionsClient := suggestions.NewClient(appID, apiKey, suggestions.US)
suggestionsClient, err := suggestions.NewClient(appID, apiKey, suggestions.US)
if err != nil {
panic(err)
}

// if there is no params for the requests, we don't need to give empty request instance such as `suggestionsClient.NewApiGetAllConfigsRequest()`.
querySuggestionsIndex, err := suggestionsClient.GetAllConfigs()
Expand Down
8 changes: 6 additions & 2 deletions playground/go/recommend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import (
"fmt"

"github.com/algolia/algoliasearch-client-go/v4/algolia/recommend"
"github.com/algolia/algoliasearch-client-go/v4/algolia/utils"
)

func testRecommend(appID, apiKey string) int {
recommendClient := recommend.NewClient(appID, apiKey)
recommendClient, err := recommend.NewClient(appID, apiKey)
if err != nil {
panic(err)
}

/*
recommend.NewGetRecommendationsParams([]recommend.RecommendationsRequest{
Expand All @@ -22,7 +26,7 @@ func testRecommend(appID, apiKey string) int {
Model: recommend.RECOMMENDATIONMODELS_BOUGHT_TOGETHER,
ObjectID: "test_query",
IndexName: "test_index",
Threshold: recommend.PtrInt32(0),
Threshold: utils.PtrInt32(0),
},
},
},
Expand Down
7 changes: 5 additions & 2 deletions playground/go/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (

func testSearch(appID, apiKey string) int {
indexName := getEnvWithDefault("SEARCH_INDEX", "test_index")
searchClient := search.NewClient(appID, apiKey)
searchClient, err := search.NewClient(appID, apiKey)
if err != nil {
panic(err)
}

response, err := searchClient.AddOrUpdateObject(
searchClient.NewApiAddOrUpdateObjectRequest(
Expand Down Expand Up @@ -40,7 +43,7 @@ func testSearch(appID, apiKey string) int {
searchClient.NewApiSearchRequest(
search.NewSearchMethodParams(
[]search.SearchQuery{
search.SearchForHitsAsSearchQuery(
*search.SearchForHitsAsSearchQuery(
search.NewSearchForHits(
indexName,
search.WithSearchForHitsQuery("foo"),
Expand Down
4 changes: 2 additions & 2 deletions specs/search/helpers/generateSecuredApiKey.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ securedAPIKeyRestrictions:
For example, if you set the filter group:admin on your generated API key, and you add groups:press OR groups:visitors with the filters query parameter, your final search filter is equivalent to groups:admin AND (groups:press OR groups:visitors).
validUntil:
type: number
format: duration
type: integer
format: int64
description: Unix timestamp used to set the expiration date of the API key.
restrictIndices:
type: array
Expand Down
4 changes: 4 additions & 0 deletions templates/go/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import (
"slices"
"time"
"github.com/algolia/algoliasearch-client-go/v4/algolia/errs"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
{{/isSearchClient}}

"github.com/algolia/algoliasearch-client-go/v4/algolia/utils"
Expand Down
58 changes: 58 additions & 0 deletions templates/go/search_helpers.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,61 @@ func (c *APIClient) WaitForApiKeyWithContext(
maxDelay,
)
}

// GenerateSecuredApiKey generates a public API key intended to restrict access
// to certain records. This new key is built upon the existing key named
// `parentApiKey` and the following options:
func (c *APIClient) GenerateSecuredApiKey(parentApiKey string, restrictions *SecuredAPIKeyRestrictions) (string, error) {
h := hmac.New(sha256.New, []byte(parentApiKey))
message, err := encodeRestrictions(restrictions)
if err != nil {
return "", err
}
_, err = h.Write([]byte(message))
if err != nil {
return "", fmt.Errorf("failed to compute HMAC: %w", err)
}

checksum := hex.EncodeToString(h.Sum(nil))
key := base64.StdEncoding.EncodeToString([]byte(checksum + message))

return key, nil
}

func encodeRestrictions(restrictions *SecuredAPIKeyRestrictions) (string, error) {
toSerialize := map[string]any{}
if restrictions.Filters != nil {
toSerialize["filters"] = *restrictions.Filters
}
if restrictions.ValidUntil != nil {
toSerialize["validUntil"] = *restrictions.ValidUntil
}
if restrictions.RestrictIndices != nil {
toSerialize["restrictIndices"] = restrictions.RestrictIndices
}
if restrictions.RestrictSources != nil {
toSerialize["restrictSources"] = *restrictions.RestrictSources
}
if restrictions.UserToken != nil {
toSerialize["userToken"] = *restrictions.UserToken
}
if restrictions.SearchParams != nil {
// merge with searchParams
serializedParams, err := restrictions.SearchParams.MarshalJSON()
if err != nil {
return "", fmt.Errorf("failed to marshal SearchParams: %w", err)
}
err = json.Unmarshal(serializedParams, &toSerialize)
if err != nil {
return "", fmt.Errorf("failed to unmarshal SearchParams: %w", err)
}
}

queryString := make([]string, 0, len(toSerialize))
for k, v := range toSerialize {
queryString = append(queryString, k+"="+queryParameterToString(v))
}

return strings.Join(queryString, "&"), nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class SecuredApiKeyHelpersTests: XCTestCase {
parentApiKey: "parent-api-key",
with: SecuredAPIKeyRestrictions(
searchParams: SearchParamsObject(hitsPerPage: 2),
validUntil: now + .seconds(13),
validUntil: Int64(now + .seconds(13)),
restrictIndices: ["index1", "index2"]
)
)
Expand Down

0 comments on commit 7a2d369

Please sign in to comment.