mirror of
https://github.com/alibaba/higress.git
synced 2026-02-06 23:21:08 +08:00
307 lines
10 KiB
C++
307 lines
10 KiB
C++
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// modified base on envoy/source/extensions/filters/http/jwt_authn/extractor.cc
|
|
#include "extensions/jwt_auth/extractor.h"
|
|
|
|
#include <memory>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
|
|
#include "absl/container/btree_map.h"
|
|
#include "common/http_util.h"
|
|
#include "extensions/jwt_auth/plugin.h"
|
|
|
|
#ifdef NULL_PLUGIN
|
|
|
|
namespace proxy_wasm {
|
|
namespace null_plugin {
|
|
namespace jwt_auth {
|
|
|
|
#endif
|
|
|
|
namespace {
|
|
/**
|
|
* Check Claims specified in Provider
|
|
*/
|
|
class JwtClaimChecker {
|
|
public:
|
|
JwtClaimChecker(const ClaimsMap& claims) : allowed_claims_(claims) {}
|
|
|
|
// check if a jwt issuer is allowed
|
|
bool check(const std::string& key, const std::string& value) const {
|
|
if (allowed_claims_.empty()) {
|
|
return true;
|
|
}
|
|
auto it = allowed_claims_.find(key);
|
|
return it != allowed_claims_.end() && it->second == value;
|
|
}
|
|
|
|
private:
|
|
// Only these specified claims are allowed.
|
|
const ClaimsMap& allowed_claims_;
|
|
};
|
|
|
|
using JwtClaimCheckerPtr = std::unique_ptr<JwtClaimChecker>;
|
|
|
|
// A base JwtLocation object to store token and claim_checker.
|
|
class JwtLocationBase : public JwtLocation {
|
|
public:
|
|
JwtLocationBase(const std::string& token,
|
|
const JwtClaimChecker& claim_checker)
|
|
: token_(token), claim_checker_(claim_checker) {}
|
|
|
|
// Get the token string
|
|
const std::string& token() const override { return token_; }
|
|
|
|
// Check if an claim has specified the location.
|
|
bool isClaimAllowed(const std::string& key,
|
|
const std::string& value) const override {
|
|
return claim_checker_.check(key, value);
|
|
}
|
|
|
|
void addClaimToHeader(const std::string& header, const std::string& value,
|
|
bool override) const override {
|
|
claims_to_headers_.emplace_back(header, value, override);
|
|
}
|
|
|
|
void claimsToHeaders() const override {
|
|
for (const auto& claim_to_header : claims_to_headers_) {
|
|
const auto& header_key = std::get<0>(claim_to_header);
|
|
const auto& header_value = std::get<1>(claim_to_header);
|
|
if (std::get<2>(claim_to_header)) {
|
|
auto header_ptr = getRequestHeader(header_key);
|
|
if (!header_ptr->view().empty()) {
|
|
replaceRequestHeader(header_key, header_value);
|
|
continue;
|
|
}
|
|
}
|
|
addRequestHeader(header_key, header_value);
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::vector<std::tuple<std::string, std::string, bool>>
|
|
claims_to_headers_;
|
|
// Extracted token.
|
|
const std::string token_;
|
|
// Claim checker
|
|
const JwtClaimChecker& claim_checker_;
|
|
};
|
|
|
|
// The JwtLocation for header extraction.
|
|
class JwtHeaderLocation : public JwtLocationBase {
|
|
public:
|
|
JwtHeaderLocation(const std::string& token,
|
|
const JwtClaimChecker& claim_checker,
|
|
const std::string& header)
|
|
: JwtLocationBase(token, claim_checker), header_(header) {}
|
|
|
|
void removeJwt() const override { removeRequestHeader(header_); }
|
|
|
|
private:
|
|
// the header name the JWT is extracted from.
|
|
const std::string& header_;
|
|
};
|
|
|
|
// The JwtLocation for param extraction.
|
|
class JwtParamLocation : public JwtLocationBase {
|
|
public:
|
|
JwtParamLocation(const std::string& token,
|
|
const JwtClaimChecker& claim_checker, const std::string&)
|
|
: JwtLocationBase(token, claim_checker) {}
|
|
|
|
void removeJwt() const override {
|
|
// TODO(qiwzhang): remove JWT from parameter.
|
|
}
|
|
};
|
|
|
|
// The JwtLocation for cookie extraction.
|
|
class JwtCookieLocation : public JwtLocationBase {
|
|
public:
|
|
JwtCookieLocation(const std::string& token,
|
|
const JwtClaimChecker& claim_checker)
|
|
: JwtLocationBase(token, claim_checker) {}
|
|
|
|
void removeJwt() const override {
|
|
// TODO(theshubhamp): remove JWT from cookies.
|
|
}
|
|
};
|
|
|
|
class ExtractorImpl : public Extractor {
|
|
public:
|
|
ExtractorImpl(const Consumer& provider);
|
|
|
|
std::vector<JwtLocationConstPtr> extract() const override;
|
|
|
|
private:
|
|
// add a header config
|
|
void addHeaderConfig(const ClaimsMap& claims, const std::string& header_name,
|
|
const std::string& value_prefix);
|
|
// add a query param config
|
|
void addQueryParamConfig(const ClaimsMap& claims, const std::string& param);
|
|
// add a query param config
|
|
void addCookieConfig(const ClaimsMap& claims, const std::string& cookie);
|
|
// ctor helper for a jwt provider config
|
|
void addProvider(const Consumer& provider);
|
|
|
|
// HeaderMap value type to store prefix and issuers that specified this
|
|
// header.
|
|
struct HeaderLocationSpec {
|
|
HeaderLocationSpec(const std::string& header,
|
|
const std::string& value_prefix)
|
|
: header_(header), value_prefix_(value_prefix) {}
|
|
// The header name.
|
|
std::string header_;
|
|
// The value prefix. e.g. for "Bearer <token>", the value_prefix is "Bearer
|
|
// ".
|
|
std::string value_prefix_;
|
|
// Issuers that specified this header.
|
|
JwtClaimCheckerPtr claim_checker_;
|
|
};
|
|
using HeaderLocationSpecPtr = std::unique_ptr<HeaderLocationSpec>;
|
|
// The map of (header + value_prefix) to HeaderLocationSpecPtr
|
|
std::map<std::string, HeaderLocationSpecPtr> header_locations_;
|
|
|
|
// ParamMap value type to store issuers that specified this header.
|
|
struct ParamLocationSpec {
|
|
// Issuers that specified this param.
|
|
JwtClaimCheckerPtr claim_checker_;
|
|
};
|
|
// The map of a parameter key to set of issuers specified the parameter
|
|
std::map<std::string, ParamLocationSpec> param_locations_;
|
|
|
|
// CookieMap value type to store issuers that specified this cookie.
|
|
struct CookieLocationSpec {
|
|
// Issuers that specified this param.
|
|
JwtClaimCheckerPtr claim_checker_;
|
|
};
|
|
// The map of a cookie key to set of issuers specified the cookie.
|
|
absl::btree_map<std::string, CookieLocationSpec> cookie_locations_;
|
|
};
|
|
|
|
ExtractorImpl::ExtractorImpl(const Consumer& provider) {
|
|
addProvider(provider);
|
|
}
|
|
|
|
void ExtractorImpl::addProvider(const Consumer& provider) {
|
|
for (const auto& header : provider.from_headers) {
|
|
addHeaderConfig(provider.allowd_claims, header.header, header.value_prefix);
|
|
}
|
|
for (const std::string& param : provider.from_params) {
|
|
addQueryParamConfig(provider.allowd_claims, param);
|
|
}
|
|
for (const std::string& cookie : provider.from_cookies) {
|
|
addCookieConfig(provider.allowd_claims, cookie);
|
|
}
|
|
}
|
|
|
|
void ExtractorImpl::addHeaderConfig(const ClaimsMap& claims,
|
|
const std::string& header_name,
|
|
const std::string& value_prefix) {
|
|
const std::string map_key = header_name + value_prefix;
|
|
auto& header_location_spec = header_locations_[map_key];
|
|
if (!header_location_spec) {
|
|
header_location_spec =
|
|
std::make_unique<HeaderLocationSpec>(header_name, value_prefix);
|
|
}
|
|
header_location_spec->claim_checker_ =
|
|
std::make_unique<JwtClaimChecker>(claims);
|
|
}
|
|
|
|
void ExtractorImpl::addQueryParamConfig(const ClaimsMap& claims,
|
|
const std::string& param) {
|
|
auto& param_location_spec = param_locations_[param];
|
|
param_location_spec.claim_checker_ =
|
|
std::make_unique<JwtClaimChecker>(claims);
|
|
}
|
|
|
|
void ExtractorImpl::addCookieConfig(const ClaimsMap& claims,
|
|
const std::string& cookie) {
|
|
auto& cookie_location_spec = cookie_locations_[cookie];
|
|
cookie_location_spec.claim_checker_ =
|
|
std::make_unique<JwtClaimChecker>(claims);
|
|
}
|
|
|
|
std::vector<JwtLocationConstPtr> ExtractorImpl::extract() const {
|
|
std::vector<JwtLocationConstPtr> tokens;
|
|
|
|
// Check header locations first
|
|
for (const auto& location_it : header_locations_) {
|
|
const auto& location_spec = location_it.second;
|
|
|
|
auto header = getRequestHeader(location_spec->header_)->toString();
|
|
if (!header.empty()) {
|
|
const auto pos = header.find(location_spec->value_prefix_);
|
|
if (pos == std::string::npos) {
|
|
continue;
|
|
}
|
|
auto header_strip =
|
|
header.substr(pos + location_spec->value_prefix_.length());
|
|
tokens.push_back(std::make_unique<const JwtHeaderLocation>(
|
|
header_strip, *location_spec->claim_checker_,
|
|
location_spec->header_));
|
|
}
|
|
}
|
|
|
|
// Check query parameter locations only if query parameter locations specified
|
|
// and Path() is not null
|
|
auto path = getRequestHeader(Wasm::Common::Http::Header::Path)->toString();
|
|
if (!param_locations_.empty() && !path.empty()) {
|
|
const auto& params = Wasm::Common::Http::parseAndDecodeQueryString(path);
|
|
for (const auto& location_it : param_locations_) {
|
|
const auto& param_key = location_it.first;
|
|
const auto& location_spec = location_it.second;
|
|
const auto& it = params.find(param_key);
|
|
if (it != params.end()) {
|
|
tokens.push_back(std::make_unique<const JwtParamLocation>(
|
|
it->second, *location_spec.claim_checker_, param_key));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check cookie locations.
|
|
if (!cookie_locations_.empty()) {
|
|
const auto& cookies =
|
|
Wasm::Common::Http::parseCookies([&](absl::string_view k) -> bool {
|
|
return cookie_locations_.contains(k);
|
|
});
|
|
|
|
for (const auto& location_it : cookie_locations_) {
|
|
const auto& cookie_key = location_it.first;
|
|
const auto& location_spec = location_it.second;
|
|
const auto& it = cookies.find(cookie_key);
|
|
if (it != cookies.end()) {
|
|
tokens.push_back(std::make_unique<const JwtCookieLocation>(
|
|
it->second, *location_spec.claim_checker_));
|
|
}
|
|
}
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ExtractorConstPtr Extractor::create(const Consumer& provider) {
|
|
return std::make_unique<ExtractorImpl>(provider);
|
|
}
|
|
|
|
#ifdef NULL_PLUGIN
|
|
|
|
} // namespace jwt_auth
|
|
} // namespace null_plugin
|
|
} // namespace proxy_wasm
|
|
|
|
#endif
|