Coverage Report

Created: 2023-11-12 09:30

/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