-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Creating JwksFetcher interface and impl v2 #4242
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
Changes from all commits
7914794
d3fbad5
c90ebf7
1b2e6c7
6dbc828
6ca0471
498c858
af9175e
51c4d1b
f477905
8fa1dc8
2d18a01
1d9281e
9d277be
4c77200
4bb61d2
8c1c294
489c52d
17ddf46
8038924
1457fb1
f95e51e
18b6b9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| #include "extensions/filters/http/common/jwks_fetcher.h" | ||
|
|
||
| #include "common/common/enum_to_int.h" | ||
| #include "common/http/headers.h" | ||
| #include "common/http/utility.h" | ||
|
|
||
| #include "jwt_verify_lib/status.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace HttpFilters { | ||
| namespace Common { | ||
| namespace { | ||
|
|
||
| class JwksFetcherImpl : public JwksFetcher, | ||
| public Logger::Loggable<Logger::Id::filter>, | ||
| public Http::AsyncClient::Callbacks { | ||
| public: | ||
| JwksFetcherImpl(Upstream::ClusterManager& cm) : cm_(cm) { ENVOY_LOG(trace, "{}", __func__); } | ||
|
|
||
| ~JwksFetcherImpl() { cancel(); } | ||
|
|
||
| void cancel() { | ||
| if (request_ && !complete_) { | ||
| request_->cancel(); | ||
| ENVOY_LOG(debug, "fetch pubkey [uri = {}]: canceled", uri_->uri()); | ||
| } | ||
| reset(); | ||
| } | ||
|
|
||
| void fetch(const ::envoy::api::v2::core::HttpUri& uri, JwksFetcher::JwksReceiver& receiver) { | ||
| ENVOY_LOG(trace, "{}", __func__); | ||
| ASSERT(!receiver_); | ||
| complete_ = false; | ||
| receiver_ = &receiver; | ||
| uri_ = &uri; | ||
| Http::MessagePtr message = Http::Utility::prepareHeaders(uri); | ||
| message->headers().insertMethod().value().setReference(Http::Headers::get().MethodValues.Get); | ||
| ENVOY_LOG(debug, "fetch pubkey from [uri = {}]: start", uri_->uri()); | ||
| request_ = | ||
| cm_.httpAsyncClientForCluster(uri.cluster()) | ||
| .send(std::move(message), *this, | ||
| std::chrono::milliseconds(DurationUtil::durationToMilliseconds(uri.timeout()))); | ||
| } | ||
|
|
||
| // HTTP async receive methods | ||
| void onSuccess(Http::MessagePtr&& response) { | ||
| ENVOY_LOG(trace, "{}", __func__); | ||
| complete_ = true; | ||
| const uint64_t status_code = Http::Utility::getResponseStatus(response->headers()); | ||
| if (status_code == enumToInt(Http::Code::OK)) { | ||
| ENVOY_LOG(debug, "{}: fetch pubkey [uri = {}]: success", __func__, uri_->uri()); | ||
| if (response->body()) { | ||
| const auto len = response->body()->length(); | ||
| const auto body = std::string(static_cast<char*>(response->body()->linearize(len)), len); | ||
| auto jwks = | ||
| google::jwt_verify::Jwks::createFrom(body, google::jwt_verify::Jwks::Type::JWKS); | ||
| if (jwks->getStatus() == google::jwt_verify::Status::Ok) { | ||
| ENVOY_LOG(debug, "{}: fetch pubkey [uri = {}]: succeeded", __func__, uri_->uri()); | ||
| receiver_->onJwksSuccess(std::move(jwks)); | ||
| } else { | ||
| ENVOY_LOG(debug, "{}: fetch pubkey [uri = {}]: invalid jwks", __func__, uri_->uri()); | ||
| receiver_->onJwksError(JwksFetcher::JwksReceiver::Failure::InvalidJwks); | ||
| } | ||
| } else { | ||
| ENVOY_LOG(debug, "{}: fetch pubkey [uri = {}]: body is empty", __func__, uri_->uri()); | ||
| receiver_->onJwksError(JwksFetcher::JwksReceiver::Failure::Network); | ||
| } | ||
| } else { | ||
| ENVOY_LOG(debug, "{}: fetch pubkey [uri = {}]: response status code {}", __func__, | ||
| uri_->uri(), status_code); | ||
| receiver_->onJwksError(JwksFetcher::JwksReceiver::Failure::Network); | ||
| } | ||
| reset(); | ||
| } | ||
|
|
||
| void onFailure(Http::AsyncClient::FailureReason reason) { | ||
| ENVOY_LOG(debug, "{}: fetch pubkey [uri = {}]: network error {}", __func__, uri_->uri(), | ||
| enumToInt(reason)); | ||
| complete_ = true; | ||
| receiver_->onJwksError(JwksFetcher::JwksReceiver::Failure::Network); | ||
| reset(); | ||
| } | ||
|
|
||
| private: | ||
| Upstream::ClusterManager& cm_; | ||
| bool complete_{}; | ||
| JwksFetcher::JwksReceiver* receiver_{}; | ||
| const envoy::api::v2::core::HttpUri* uri_{}; | ||
| Http::AsyncClient::Request* request_{}; | ||
|
|
||
| void reset() { | ||
| request_ = nullptr; | ||
| receiver_ = nullptr; | ||
| uri_ = nullptr; | ||
| } | ||
| }; | ||
| } // namespace | ||
|
|
||
| JwksFetcherPtr JwksFetcher::create(Upstream::ClusterManager& cm) { | ||
| return std::make_unique<JwksFetcherImpl>(cm); | ||
| } | ||
| } // namespace Common | ||
| } // namespace HttpFilters | ||
| } // namespace Extensions | ||
| } // namespace Envoy | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #pragma once | ||
|
|
||
| #include "envoy/api/v2/core/http_uri.pb.h" | ||
| #include "envoy/common/pure.h" | ||
| #include "envoy/upstream/cluster_manager.h" | ||
|
|
||
| #include "jwt_verify_lib/jwks.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace HttpFilters { | ||
| namespace Common { | ||
|
|
||
| class JwksFetcher; | ||
| typedef std::unique_ptr<JwksFetcher> JwksFetcherPtr; | ||
| /** | ||
| * JwksFetcher interface can be used to retrieve remote JWKS | ||
| * (https://tools.ietf.org/html/rfc7517) data structures returning a concrete, | ||
| * type-safe representation. An instance of this interface is designed to | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, you answer the above question here. This is a pretty weird pattern; I would call it out by explicitly naming the class/interface
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| * retrieve one JWKS at a time. | ||
| */ | ||
| class JwksFetcher { | ||
| public: | ||
| class JwksReceiver { | ||
| public: | ||
| enum class Failure { | ||
| /* A network error occured causing JWKS retrieval failure. */ | ||
| Network, | ||
| /* A failure occured when trying to parse the retrieved JWKS data. */ | ||
| InvalidJwks, | ||
| }; | ||
|
|
||
| virtual ~JwksReceiver(){}; | ||
| /* | ||
| * Successful retrieval callback. | ||
| * of the returned JWKS object. | ||
| * @param jwks the JWKS object retrieved. | ||
| */ | ||
| virtual void onJwksSuccess(google::jwt_verify::JwksPtr&& jwks) PURE; | ||
| /* | ||
| * Retrieval error callback. | ||
| * * @param reason the failure reason. | ||
| */ | ||
| virtual void onJwksError(Failure reason) PURE; | ||
| }; | ||
|
|
||
| virtual ~JwksFetcher(){}; | ||
|
|
||
| /* | ||
| * Cancel any in-flight request. | ||
| */ | ||
| virtual void cancel() PURE; | ||
|
|
||
| /* | ||
| * Retrieve a JWKS resource from a remote HTTP host. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 2d18a01 |
||
| * At most one outstanding request may be in-flight, | ||
| * i.e. from the invocation of `fetch()` until either | ||
| * a callback or `cancel()` is invoked, no | ||
| * additional `fetch()` may be issued. | ||
| * @param uri the uri to retrieve the jwks from. | ||
| * @param receiver the receiver of the fetched JWKS or error. | ||
| */ | ||
| virtual void fetch(const ::envoy::api::v2::core::HttpUri& uri, JwksReceiver& receiver) PURE; | ||
|
|
||
| /* | ||
| * Factory method for creating a JwksFetcher. | ||
| * @param cm the cluster manager to use during Jwks retrieval | ||
| * @return a JwksFetcher instance | ||
| */ | ||
| static JwksFetcherPtr create(Upstream::ClusterManager& cm); | ||
| }; | ||
| } // namespace Common | ||
| } // namespace HttpFilters | ||
| } // namespace Extensions | ||
| } // namespace Envoy | ||
Uh oh!
There was an error while loading. Please reload this page.