Skip to content
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
1 change: 1 addition & 0 deletions docs/root/intro/arch_overview/security/rbac_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ The following attributes are exposed to the language runtime:
destination.address, string, Downstream connection local address
destination.port, int, Downstream connection local port
metadata, :ref:`Metadata<envoy_api_msg_core.Metadata>`, Dynamic metadata
filter_state, map string to bytes, Filter state mapping data names to their serialized string value
connection.mtls, bool, Indicates whether TLS is applied to the downstream connection and the peer ceritificate is presented
connection.requested_server_name, string, Requested server name in the downstream TLS connection
connection.tls_version, string, TLS version of the downstream TLS connection
Expand Down
8 changes: 7 additions & 1 deletion include/envoy/stream_info/filter_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ class FilterState {
return *result;
}

/**
* @param data_name the name of the data being looked up (mutable/readonly).
* @return a const reference to the stored data.
* An exception will be thrown if the data does not exist.
*/
virtual const Object* getDataReadOnlyGeneric(absl::string_view data_name) const PURE;

/**
* @param data_name the name of the data being looked up (mutable only).
* @return a non-const reference to the stored data if and only if the
Expand Down Expand Up @@ -154,7 +161,6 @@ class FilterState {
virtual FilterStateSharedPtr parent() const PURE;

protected:
virtual const Object* getDataReadOnlyGeneric(absl::string_view data_name) const PURE;
virtual Object* getDataMutableGeneric(absl::string_view data_name) PURE;
};

Expand Down
2 changes: 2 additions & 0 deletions source/common/router/string_accessor_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class StringAccessorImpl : public StringAccessor {
return message;
}

absl::optional<std::string> serializeAsString() const override { return value_; }

private:
std::string value_;
};
Expand Down
16 changes: 16 additions & 0 deletions source/extensions/filters/common/expr/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,22 @@ absl::optional<CelValue> PeerWrapper::operator[](CelValue key) const {
return {};
}

absl::optional<CelValue> FilterStateWrapper::operator[](CelValue key) const {
if (!key.IsString()) {
return {};
}
auto value = key.StringOrDie().value();
if (filter_state_.hasDataWithName(value)) {
const StreamInfo::FilterState::Object* object = filter_state_.getDataReadOnlyGeneric(value);
absl::optional<std::string> serialized = object->serializeAsString();
if (serialized.has_value()) {
std::string* out = ProtobufWkt::Arena::Create<std::string>(arena_, serialized.value());
return CelValue::CreateBytes(out);
}
}
return {};
}

} // namespace Expr
} // namespace Common
} // namespace Filters
Expand Down
21 changes: 20 additions & 1 deletion source/extensions/filters/common/expr/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ constexpr absl::string_view GrpcStatus = "grpc_status";
// Per-request or per-connection metadata
constexpr absl::string_view Metadata = "metadata";

// Per-request or per-connection filter state
constexpr absl::string_view FilterState = "filter_state";

// Connection properties
constexpr absl::string_view Connection = "connection";
constexpr absl::string_view MTLS = "mtls";
Expand Down Expand Up @@ -108,7 +111,14 @@ class BaseWrapper : public google::api::expr::runtime::CelMap,
const google::api::expr::runtime::CelList* ListKeys() const override {
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
}
CelValue Produce(ProtobufWkt::Arena*) override { return CelValue::CreateMap(this); }
CelValue Produce(ProtobufWkt::Arena* arena) override {
// Producer is unique per evaluation arena since activation is re-created.
arena_ = arena;
return CelValue::CreateMap(this);
}

protected:
ProtobufWkt::Arena* arena_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this being used now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serializeString returns a string by value, so a copy is made on this arena, because CelValue takes const string*.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but this looks like just a pointer to the arena that gets assigned above, how is the copy done?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arena_ is used in the implementation of the wrapper. The base class is shared with other wrappers, which is how it gets passed down. I am working on making this flow better upstream, and get rid of the arena use in this case, but it's not simple.

};

class RequestWrapper : public BaseWrapper {
Expand Down Expand Up @@ -174,6 +184,15 @@ class MetadataProducer : public google::api::expr::runtime::CelValueProducer {
const envoy::config::core::v3::Metadata& metadata_;
};

class FilterStateWrapper : public BaseWrapper {
public:
FilterStateWrapper(const StreamInfo::FilterState& filter_state) : filter_state_(filter_state) {}
absl::optional<CelValue> operator[](CelValue key) const override;

private:
const StreamInfo::FilterState& filter_state_;
};

} // namespace Expr
} // namespace Common
} // namespace Filters
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/filters/common/expr/evaluator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ ActivationPtr createActivation(const StreamInfo::StreamInfo& info,
activation->InsertValueProducer(Destination, std::make_unique<PeerWrapper>(info, true));
activation->InsertValueProducer(Metadata,
std::make_unique<MetadataProducer>(info.dynamicMetadata()));
activation->InsertValueProducer(FilterState,
std::make_unique<FilterStateWrapper>(info.filterState()));
return activation;
}

Expand Down
2 changes: 2 additions & 0 deletions test/extensions/filters/common/expr/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ envoy_extension_cc_test(
srcs = ["context_test.cc"],
extension_name = "envoy.filters.http.rbac",
deps = [
"//source/common/router:string_accessor_lib",
"//source/common/stream_info:stream_info_lib",
"//source/extensions/filters/common/expr:context_lib",
"//test/mocks/ssl:ssl_mocks",
"//test/mocks/stream_info:stream_info_mocks",
Expand Down
28 changes: 28 additions & 0 deletions test/extensions/filters/common/expr/context_test.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include "common/network/utility.h"
#include "common/router/string_accessor_impl.h"
#include "common/stream_info/filter_state_impl.h"

#include "extensions/filters/common/expr/context.h"

Expand Down Expand Up @@ -606,6 +608,32 @@ TEST(Context, ConnectionAttributes) {
}
}

TEST(Context, FilterStateAttributes) {
StreamInfo::FilterStateImpl filter_state(StreamInfo::FilterState::LifeSpan::FilterChain);
FilterStateWrapper wrapper(filter_state);
ProtobufWkt::Arena arena;
wrapper.Produce(&arena);

const std::string key = "filter_state_key";
const std::string serialized = "filter_state_value";
const std::string missing = "missing_key";

auto accessor = std::make_shared<Envoy::Router::StringAccessorImpl>(serialized);
filter_state.setData(key, accessor, StreamInfo::FilterState::StateType::ReadOnly);

{
auto value = wrapper[CelValue::CreateStringView(missing)];
EXPECT_FALSE(value.has_value());
}

{
auto value = wrapper[CelValue::CreateStringView(key)];
EXPECT_TRUE(value.has_value());
EXPECT_TRUE(value.value().IsBytes());
EXPECT_EQ(serialized, value.value().BytesOrDie().value());
}
}

} // namespace
} // namespace Expr
} // namespace Common
Expand Down