/proc/self/cwd/source/extensions/filters/common/rbac/engine_impl.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/extensions/filters/common/rbac/engine_impl.h" |
2 | | |
3 | | #include "envoy/config/rbac/v3/rbac.pb.h" |
4 | | #include "envoy/config/rbac/v3/rbac.pb.validate.h" |
5 | | |
6 | | #include "source/common/http/header_map_impl.h" |
7 | | |
8 | | namespace Envoy { |
9 | | namespace Extensions { |
10 | | namespace Filters { |
11 | | namespace Common { |
12 | | namespace RBAC { |
13 | | |
14 | | Envoy::Matcher::ActionFactoryCb |
15 | | ActionFactory::createActionFactoryCb(const Protobuf::Message& config, ActionContext& context, |
16 | 0 | ProtobufMessage::ValidationVisitor& validation_visitor) { |
17 | 0 | const auto& action_config = |
18 | 0 | MessageUtil::downcastAndValidate<const envoy::config::rbac::v3::Action&>(config, |
19 | 0 | validation_visitor); |
20 | 0 | const auto& name = action_config.name(); |
21 | 0 | const auto action = action_config.action(); |
22 | | |
23 | | // If there is at least an action is LOG, we have to go through LOG procedure when handle action. |
24 | 0 | if (action == envoy::config::rbac::v3::RBAC::LOG) { |
25 | 0 | context.has_log_ = true; |
26 | 0 | } |
27 | |
|
28 | 0 | return [name, action]() { return std::make_unique<Action>(name, action); }; |
29 | 0 | } |
30 | | |
31 | | REGISTER_FACTORY(ActionFactory, Envoy::Matcher::ActionFactory<ActionContext>); |
32 | | |
33 | 6.03k | void generateLog(StreamInfo::StreamInfo& info, EnforcementMode mode, bool log) { |
34 | | // If not shadow enforcement, set shared log metadata. |
35 | 6.03k | if (mode != EnforcementMode::Shadow) { |
36 | 3.26k | ProtobufWkt::Struct log_metadata; |
37 | 3.26k | auto& log_fields = *log_metadata.mutable_fields(); |
38 | 3.26k | log_fields[DynamicMetadataKeysSingleton::get().AccessLogKey].set_bool_value(log); |
39 | 3.26k | info.setDynamicMetadata(DynamicMetadataKeysSingleton::get().CommonNamespace, log_metadata); |
40 | 3.26k | } |
41 | 6.03k | } |
42 | | |
43 | | RoleBasedAccessControlEngineImpl::RoleBasedAccessControlEngineImpl( |
44 | | const envoy::config::rbac::v3::RBAC& rules, |
45 | | ProtobufMessage::ValidationVisitor& validation_visitor, const EnforcementMode mode) |
46 | 4.33k | : action_(rules.action()), mode_(mode) { |
47 | | // guard expression builder by presence of a condition in policies |
48 | 4.40k | for (const auto& policy : rules.policies()) { |
49 | 4.40k | if (policy.second.has_condition()) { |
50 | 3.90k | builder_ = Expr::createBuilder(&constant_arena_); |
51 | 3.90k | break; |
52 | 3.90k | } |
53 | 4.40k | } |
54 | | |
55 | 4.72k | for (const auto& policy : rules.policies()) { |
56 | 4.72k | policies_.emplace(policy.first, std::make_unique<PolicyMatcher>(policy.second, builder_.get(), |
57 | 4.72k | validation_visitor)); |
58 | 4.72k | } |
59 | 4.33k | } |
60 | | |
61 | | bool RoleBasedAccessControlEngineImpl::handleAction(const Network::Connection& connection, |
62 | | StreamInfo::StreamInfo& info, |
63 | 10.8k | std::string* effective_policy_id) const { |
64 | 10.8k | return handleAction(connection, *Http::StaticEmptyHeaders::get().request_headers, info, |
65 | 10.8k | effective_policy_id); |
66 | 10.8k | } |
67 | | |
68 | | bool RoleBasedAccessControlEngineImpl::handleAction(const Network::Connection& connection, |
69 | | const Envoy::Http::RequestHeaderMap& headers, |
70 | | StreamInfo::StreamInfo& info, |
71 | 12.7k | std::string* effective_policy_id) const { |
72 | 12.7k | bool matched = checkPolicyMatch(connection, info, headers, effective_policy_id); |
73 | | |
74 | 12.7k | switch (action_) { |
75 | 0 | PANIC_ON_PROTO_ENUM_SENTINEL_VALUES; |
76 | 4.92k | case envoy::config::rbac::v3::RBAC::ALLOW: |
77 | 4.92k | return matched; |
78 | 1.83k | case envoy::config::rbac::v3::RBAC::DENY: |
79 | 1.83k | return !matched; |
80 | 6.03k | case envoy::config::rbac::v3::RBAC::LOG: { |
81 | 6.03k | generateLog(info, mode_, matched); |
82 | | |
83 | 6.03k | return true; |
84 | 0 | } |
85 | 12.7k | } |
86 | 0 | PANIC_DUE_TO_CORRUPT_ENUM; |
87 | 0 | } |
88 | | |
89 | | bool RoleBasedAccessControlEngineImpl::checkPolicyMatch( |
90 | | const Network::Connection& connection, const StreamInfo::StreamInfo& info, |
91 | 12.7k | const Envoy::Http::RequestHeaderMap& headers, std::string* effective_policy_id) const { |
92 | 12.7k | bool matched = false; |
93 | | |
94 | 15.0k | for (const auto& policy : policies_) { |
95 | 15.0k | if (policy.second->matches(connection, headers, info)) { |
96 | 2.83k | matched = true; |
97 | 2.83k | if (effective_policy_id != nullptr) { |
98 | 2.83k | *effective_policy_id = policy.first; |
99 | 2.83k | } |
100 | 2.83k | break; |
101 | 2.83k | } |
102 | 15.0k | } |
103 | | |
104 | 12.7k | return matched; |
105 | 12.7k | } |
106 | | |
107 | | RoleBasedAccessControlMatcherEngineImpl::RoleBasedAccessControlMatcherEngineImpl( |
108 | | const xds::type::matcher::v3::Matcher& matcher, |
109 | | Server::Configuration::ServerFactoryContext& factory_context, |
110 | | ActionValidationVisitor& validation_visitor, const EnforcementMode mode) |
111 | 99 | : mode_(mode) { |
112 | 99 | ActionContext context{false}; |
113 | 99 | Envoy::Matcher::MatchTreeFactory<Http::HttpMatchingData, ActionContext> factory( |
114 | 99 | context, factory_context, validation_visitor); |
115 | 99 | matcher_ = factory.create(matcher)(); |
116 | 99 | has_log_ = context.has_log_; |
117 | | |
118 | 99 | if (!validation_visitor.errors().empty()) { |
119 | 0 | throw EnvoyException(fmt::format("requirement violation while creating RBAC match tree: {}", |
120 | 0 | validation_visitor.errors()[0])); |
121 | 0 | } |
122 | 99 | } |
123 | | |
124 | | bool RoleBasedAccessControlMatcherEngineImpl::handleAction(const Network::Connection& connection, |
125 | | StreamInfo::StreamInfo& info, |
126 | 521 | std::string* effective_policy_id) const { |
127 | 521 | return handleAction(connection, *Http::StaticEmptyHeaders::get().request_headers, info, |
128 | 521 | effective_policy_id); |
129 | 521 | } |
130 | | |
131 | | bool RoleBasedAccessControlMatcherEngineImpl::handleAction( |
132 | | const Network::Connection&, const Envoy::Http::RequestHeaderMap& headers, |
133 | 566 | StreamInfo::StreamInfo& info, std::string* effective_policy_id) const { |
134 | 566 | Http::Matching::HttpMatchingDataImpl data(info); |
135 | 566 | data.onRequestHeaders(headers); |
136 | 566 | const auto& result = Envoy::Matcher::evaluateMatch<Http::HttpMatchingData>(*matcher_, data); |
137 | 566 | ASSERT(result.match_state_ == Envoy::Matcher::MatchState::MatchComplete); |
138 | 566 | if (result.result_) { |
139 | 0 | auto action = result.result_()->getTyped<Action>(); |
140 | 0 | if (effective_policy_id != nullptr) { |
141 | 0 | *effective_policy_id = action.name(); |
142 | 0 | } |
143 | | |
144 | | // If there is at least an LOG action in matchers, we have to turn on and off for shared log |
145 | | // metadata every time when there is a connection or request. |
146 | 0 | auto rbac_action = action.action(); |
147 | 0 | if (has_log_) { |
148 | 0 | generateLog(info, mode_, rbac_action == envoy::config::rbac::v3::RBAC::LOG); |
149 | 0 | } |
150 | |
|
151 | 0 | switch (rbac_action) { |
152 | 0 | PANIC_ON_PROTO_ENUM_SENTINEL_VALUES; |
153 | 0 | case envoy::config::rbac::v3::RBAC::ALLOW: |
154 | 0 | case envoy::config::rbac::v3::RBAC::LOG: |
155 | 0 | return true; |
156 | 0 | case envoy::config::rbac::v3::RBAC::DENY: |
157 | 0 | return false; |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | // Default to DENY. |
162 | 566 | return false; |
163 | 566 | } |
164 | | |
165 | | } // namespace RBAC |
166 | | } // namespace Common |
167 | | } // namespace Filters |
168 | | } // namespace Extensions |
169 | | } // namespace Envoy |