Conversation
|
Hi @Inode1, welcome and thank you for your contribution. We will try to review your Pull Request as quickly as possible. In the meantime, please take a look at the contribution guidelines if you have not done so already. |
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
| private: | ||
| void readAndWatchSecret(std::string& value, | ||
| Secret::GenericSecretConfigProvider& secret_provider) { | ||
| void readAndWatchSecret(std::string& value, Secret::GenericSecretConfigProvider& secret_provider, |
There was a problem hiding this comment.
Could you return the update_callback from the method instead of using a parameter? I think it would be more readable...
|
Could you provide a brief explanation of how this fixes the problem in #16126? Also, could you add tests? |
|
Yup. This method secret_provider->addUpdateCallback calls this at the end. And if we don't save results in unique_ptr it will be destroyed and deleted from the callbacks list according to this. We need to create two objects here. If we don't do it the update_callback_ will be rewrited and callback will be deleted. |
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
|
Nice catch, @Inode1. Could you add a test? |
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
|
Here you are, check this out. |
|
/retest |
|
Retrying Azure Pipelines: |
|
|
||
| token_callback->onConfigUpdate(decoded_resources_token.refvec_, ""); | ||
| EXPECT_EQ(secret_reader.clientSecret(), "client_test"); | ||
| EXPECT_EQ(secret_reader.tokenSecret(), "token_test"); |
There was a problem hiding this comment.
Could you add another check where token stays the same, but the client secret is updated again? To make sure that an update to client secret doesn't overwrite the token.
|
Lgtm, just a small ask in the test. |
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
snowp
left a comment
There was a problem hiding this comment.
Thanks! Could you include a bit more information in the PR description that describes that the bug is and how this fixes it?
Also, do you think it would be possible to write an integration test for this? Would be good to have a test that verifies this end to end.
| Event::SimulatedTimeSystem test_time_; | ||
| }; | ||
|
|
||
| // Verifies that the OAuth SDSSecretReader correctly updates dynamic generic secret |
There was a problem hiding this comment.
nit: end in period to keep comments using proper grammar
| )EOF"; | ||
|
|
||
| envoy::extensions::transport_sockets::tls::v3::Secret typed_secret; | ||
| TestUtility::loadFromYaml(TestEnvironment::substitute(yaml_client), typed_secret); |
There was a problem hiding this comment.
I don't think you need to use substitute here and elsewhere since the input doesn't use any of the substitution formats.
| TEST_F(OAuth2Test, SdsDynamicGenericSecret) { | ||
| NiceMock<Server::MockConfigTracker> config_tracker; | ||
| std::unique_ptr<Secret::SecretManager> secret_manager( | ||
| new Secret::SecretManagerImpl(config_tracker)); |
There was a problem hiding this comment.
You can probably get by here by allocating this on the stack?
| NiceMock<Server::Configuration::MockTransportSocketFactoryContext> secret_context; | ||
| NiceMock<LocalInfo::MockLocalInfo> local_info; | ||
| Api::ApiPtr api = Api::createApiForTest(); | ||
| Stats::IsolatedStoreImpl stats; |
There was a problem hiding this comment.
At least this one can use the store_ defined on the fixture class instead of redefining one here
There was a problem hiding this comment.
I think it will be more readable if we leave it in the function scope to save the context.
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
|
I don't know how to update secrets in integration tests, so if you give me a hint, I think I can do it. |
|
Maybe try looking at sds_static_integration_test or sds_dynamic_integration_test for how that sets up SDS triggers? |
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
|
/retest |
|
Retrying Azure Pipelines: |
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
snowp
left a comment
There was a problem hiding this comment.
Thanks for getting an integration test together!
Re your question on thread safety it's hard to say without deeper inspection. What specifically makes you think that it's not thread safe? At a quick look it does seem problematic that we're just updating the secrets from the main thread and then presumably read from the filters? We might want to make use of a TLS slot here to avoid that, but if we can get an integration test reproducing the issue first that would be awesome.
| EXPECT_EQ("302", response->headers().getStatusValue()); | ||
| } | ||
|
|
||
| std::string parseSetCookieValue(const Http::HeaderMap& headers, const std::string& key) { |
There was a problem hiding this comment.
Can you use the helper in http/utlity.h instead of redefining this here?
| initialize(); | ||
|
|
||
| Filesystem::WatcherPtr watcher = dispatcher_->createFilesystemWatcher(); | ||
| Filesystem::Watcher::OnChangedCb on_changed_cb_; |
| TEST_F(OauthIntegrationTest, AuthenticationFlow) { | ||
| initialize(); | ||
|
|
||
| Filesystem::WatcherPtr watcher = dispatcher_->createFilesystemWatcher(); |
There was a problem hiding this comment.
Maybe extract this stuff into a setup function? Just to declutter the test file
Also: does it make sense to do one authentication flow, reload the secrets, then do another one? That way we're verifying a real world secret rotation scenario
| watcher->addWatch(TestEnvironment::temporaryPath("token_secret.yaml"), | ||
| Filesystem::Watcher::Events::MovedTo, | ||
| [&](uint32_t) -> void { dispatcher_->exit(); }); | ||
|
|
||
| watcher->addWatch(TestEnvironment::temporaryPath("hmac_secret.yaml"), | ||
| Filesystem::Watcher::Events::MovedTo, | ||
| [&](uint32_t) -> void { dispatcher_->exit(); }); | ||
|
|
||
| TestEnvironment::renameFile(TestEnvironment::temporaryPath("token_secret_1.yaml"), | ||
| TestEnvironment::temporaryPath("token_secret.yaml")); | ||
| dispatcher_->run(Event::Dispatcher::RunType::Block); | ||
| TestEnvironment::renameFile(TestEnvironment::temporaryPath("hmac_secret_1.yaml"), | ||
| TestEnvironment::temporaryPath("hmac_secret.yaml")); | ||
|
|
||
| dispatcher_->run(Event::Dispatcher::RunType::Block); |
There was a problem hiding this comment.
It might be simpler to wait for a stat to update that reflects the secret reload, assuming we have one?
| {"x-forwarded-proto", "http"}, | ||
| {":authority", "authority"}, | ||
| {"authority", "Bearer token"}}; | ||
| absl::string_view host_ = headers.Host()->value().getStringView(); |
There was a problem hiding this comment.
The _ suffix is reserved for field members, so I'd call this host instead
| const auto hmac_payload = absl::StrCat(host_, expires_, token_); | ||
| std::string hmac_secret = "hmac_secret_1"; | ||
| std::vector<uint8_t> hmac_secret_vec(hmac_secret.begin(), hmac_secret.end()); | ||
| const auto pre_encoded_hmac = | ||
| Hex::encode(crypto_util.getSha256Hmac(hmac_secret_vec, hmac_payload)); | ||
| std::string encoded_hmac; | ||
| absl::Base64Escape(pre_encoded_hmac, &encoded_hmac); |
There was a problem hiding this comment.
Putting this in a helper function might help readability, its quite a bit of code just to verify the hmac
| std::string expires_ = parseSetCookieValue(headers_, "OauthExpires"); | ||
| std::string token_ = parseSetCookieValue(headers_, "BearerToken"); | ||
| std::string hmac_ = parseSetCookieValue(headers_, "OauthHMAC"); |
There was a problem hiding this comment.
Remove the _ suffix since these aren't field variables
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
|
Thank you, @snowp your remarks were taken into consideration. So I will recheck my suspicions about thread safe and if this is confirmed I will open a new issue. |
|
/retest |
|
Retrying Azure Pipelines: |
snowp
left a comment
There was a problem hiding this comment.
Thanks this looks great, just a few more comments and then I think we're good to go
| * @param key the key for the particular set-cookie value to return | ||
| * @return std::string the parsed set-cookie value, or "" if none exists | ||
| **/ | ||
| std::string parseSetCookieValue(const HeaderMap& headers, const std::string& key); |
There was a problem hiding this comment.
Mind adding a test for this function? Ideally we have coverage for this outside of extensions. Thanks!
There was a problem hiding this comment.
I can copy-past some tests from utility_test.cpp with parseCookieValue check and trasnform them for parseSetCookieValue. Will it be okay?
|
|
||
| OAuth2CookieValidator validator{api_->timeSource()}; | ||
| validator.setParams(validate_headers, std::string(hmac_secret)); | ||
| ASSERT_TRUE(validator.isValid()); |
There was a problem hiding this comment.
if you return validator.isValid() here instead you can do an EXPECT_TRUE at call site and provide better error messaging
Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru>
The client_secret field was not updated on configuration changes. Signed-off-by: i.makarychev <i.makarychev@tinkoff.ru> Signed-off-by: James Mulcahy <jmulcahy@netflix.com>
Additional Description:
The client_secret field was not updated on configuration changes.
Risk Level: Low
Testing: n/a
Docs Changes: n/a
Release Notes: inline
Fixes #16126