Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFQ API Auth Cache #3007

Merged
merged 4 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions services/rfq/api/rest/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,22 +274,28 @@ func (r *QuoterAPIServer) checkRole(c *gin.Context, destChainID uint32) (address
return addressRecovered, err
}

hasRole := r.roleCache[destChainID].Get(addressRecovered.Hex())
// Check and update cache
cachedRoleItem := r.roleCache[destChainID].Get(addressRecovered.Hex())
var hasRole bool

if hasRole == nil || hasRole.IsExpired() {
has, roleErr := bridge.HasRole(ops, relayerRole, addressRecovered)
if roleErr == nil {
r.roleCache[destChainID].Set(addressRecovered.Hex(), has, cacheInterval)
if cachedRoleItem == nil || cachedRoleItem.IsExpired() {
// Cache miss or expired, check on-chain
hasRole, err = bridge.HasRole(ops, relayerRole, addressRecovered)
if err != nil {
return addressRecovered, fmt.Errorf("unable to check relayer role on-chain: %w", err)
}
// Update cache
r.roleCache[destChainID].Set(addressRecovered.Hex(), hasRole, cacheInterval)
} else {
// Use cached value
hasRole = cachedRoleItem.Value()
}

if roleErr != nil {
err = fmt.Errorf("unable to check relayer role on-chain")
return addressRecovered, err
} else if !has {
err = fmt.Errorf("q.Relayer not an on-chain relayer")
return addressRecovered, err
}
// Verify role
if !hasRole {
return addressRecovered, fmt.Errorf("relayer not an on-chain relayer")
}

return addressRecovered, nil
}

Expand Down
44 changes: 44 additions & 0 deletions services/rfq/api/rest/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,50 @@ func (c *ServerSuite) TestPutAndGetQuoteByRelayer() {
c.Assert().True(found, "Newly added quote not found")
}

func (c *ServerSuite) TestMultiplePutRequestsWithIncorrectAuth() {
// Start the API server in a separate goroutine and wait for it to initialize.
c.startQuoterAPIServer()

// Create a random wallet for incorrect authorization
randomWallet, err := wallet.FromRandom()
c.Require().NoError(err)

// Prepare the authorization header with a signed timestamp using the incorrect wallet
header, err := c.prepareAuthHeader(randomWallet)
c.Require().NoError(err)

// Perform multiple PUT requests to the API server with the incorrect authorization header
for i := 0; i < 3; i++ {
resp, err := c.sendPutQuoteRequest(header)
c.Require().NoError(err)
defer func() {
err = resp.Body.Close()
c.Require().NoError(err)
}()

// Read the response body
body, err := io.ReadAll(resp.Body)
c.Require().NoError(err)

// Log the response body for debugging
fmt.Printf("Request %d response: Status: %d, Body: %s\n", i+1, resp.StatusCode, string(body))

switch resp.StatusCode {
case http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden:
// These are acceptable error status codes for failed authentication
c.Assert().True(true, "Request %d correctly failed with status %d", i+1, resp.StatusCode)
case http.StatusOK:
// The ModifyQuote method returns 200 OK with an empty body on success
c.Assert().Empty(string(body), "Request %d should return an empty body on success", i+1)

// Since this shouldn't happen with incorrect auth, fail the test
c.Fail("Request %d unexpectedly succeeded, while submitting incorrect authentication", i+1)
default:
c.Fail("Unexpected status code %d for request %d", resp.StatusCode, i+1)
}
}
}

func (c *ServerSuite) TestFilterQuoteAge() {
now := time.Now()

Expand Down
Loading