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
117 changes: 76 additions & 41 deletions src/auth/Auth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,85 +179,98 @@ folly::Expected<Grants, AuthError> AuthTokenVerifier::verify(const AuthToken& to
return folly::makeUnexpected(AuthError::BadSignature);
}

folly::Expected<std::shared_ptr<const Grants>, AuthError>
folly::Expected<std::shared_ptr<const std::vector<Grants>>, AuthError>
authenticateSetup(const AuthTokenVerifier& verifier, const Parameters& setupParams) {
if (!verifier.enabled()) {
return std::shared_ptr<const Grants>{};
return std::shared_ptr<const std::vector<Grants>>{};
}
if (auto token = findAuthToken(setupParams, verifier.tokenType())) {
auto verified = verifier.verify(*token);

auto tokens = findAuthTokens(setupParams, verifier.tokenType());
std::vector<Grants> verifiedGrants;
std::optional<AuthError> firstError;
for (const auto& token : tokens) {
auto verified = verifier.verify(token);
if (verified.hasError()) {
return folly::makeUnexpected(verified.error());
if (!firstError) {
firstError = verified.error();
}
continue;
}
if (!allows(verified.value(), Action::ClientSetup, TrackNamespace{})) {
return folly::makeUnexpected(AuthError::Forbidden);
verifiedGrants.push_back(std::move(verified.value()));
}

if (!tokens.empty()) {
// At least one setup token was presented; the session is authorized iff
// any of the verified ones grants ClientSetup. All verified tokens' other
// scopes still pool into the session grants below, not just the one that
// happened to grant ClientSetup.
if (!allowsAny(verifiedGrants, Action::ClientSetup, TrackNamespace{})) {
return folly::makeUnexpected(firstError.value_or(AuthError::Forbidden));
}
return std::make_shared<const Grants>(std::move(verified.value()));
return std::make_shared<const std::vector<Grants>>(std::move(verifiedGrants));
}

if (verifier.requireSetupToken()) {
return folly::makeUnexpected(AuthError::Missing);
}
// No setup token but not required: connect with empty grants; requests must
// then carry their own tokens to be authorized.
return std::make_shared<const Grants>();
// No setup token but not required: connect with empty session grants;
// requests must then carry their own tokens to be authorized (see authorize()).
return std::make_shared<const std::vector<Grants>>();
}

folly::Expected<folly::Unit, AuthError> authorize(
const AuthTokenVerifier& verifier,
Action action,
const Parameters& params,
const TrackNamespace& ns,
const Grants& sessionGrants,
const std::vector<Grants>& sessionGrants,
std::optional<std::string_view> trackName
) {
if (!verifier.enabled()) {
return folly::unit;
}

auto token = findAuthToken(params, verifier.tokenType());
if (token && !verifier.allowRequestTokenOverride()) {
// Request carried a per-request token but override is disabled, so the
// session-setup grants govern; log rather than fail.
XLOG(DBG1) << "authorize: ignoring request AUTHORIZATION_TOKEN for action="
<< static_cast<uint64_t>(action) << " (allow_request_token_override is disabled)";
token.reset();
}

// Resolve grants from exactly one source (request token or session).
const Grants* grants = nullptr;
Grants verified;
if (token) {
auto res = verifier.verify(*token);
if (res.hasError()) {
XLOG(DBG1) << "authorize: request token verification failed for action="
<< static_cast<uint64_t>(action) << ": " << toString(res.error());
return folly::makeUnexpected(res.error());
std::vector<Grants> requestGrants;
std::optional<AuthError> firstError;
if (verifier.allowRequestTokenOverride()) {
for (const auto& token : findAuthTokens(params, verifier.tokenType())) {
auto res = verifier.verify(token);
if (res.hasError()) {
// Dropped as a non-viable candidate — another request token or a
// session grant may still cover the action.
if (!firstError) {
firstError = res.error();
}
continue;
}
requestGrants.push_back(std::move(res.value()));
}
verified = std::move(res.value());
grants = &verified;
} else {
grants = &sessionGrants;
} else if (!findAuthTokens(params, verifier.tokenType()).empty()) {
XLOG(DBG1) << "authorize: ignoring request AUTHORIZATION_TOKEN(s) for action="
<< static_cast<uint64_t>(action) << " (allow_request_token_override is disabled)";
}

const bool permitted = trackName
? allows(*grants, action, FullTrackName{ns, std::string(*trackName)})
: allows(*grants, action, ns);
const bool permitted =
trackName ? (allowsAny(requestGrants, action, FullTrackName{ns, std::string(*trackName)}) ||
allowsAny(sessionGrants, action, FullTrackName{ns, std::string(*trackName)}))
: (allowsAny(requestGrants, action, ns) || allowsAny(sessionGrants, action, ns));
if (!permitted) {
XLOG(DBG1) << "authorize: action=" << static_cast<uint64_t>(action)
<< " not permitted for ns=" << ns;
return folly::makeUnexpected(AuthError::Forbidden);
return folly::makeUnexpected(firstError.value_or(AuthError::Forbidden));
}
return folly::unit;
}

std::optional<AuthToken> findAuthToken(const Parameters& params, uint64_t tokenType) {
std::vector<AuthToken> findAuthTokens(const Parameters& params, uint64_t tokenType) {
const auto authKey = folly::to_underlying(TrackRequestParamKey::AUTHORIZATION_TOKEN);
std::vector<AuthToken> tokens;
for (const auto& param : params) {
if (param.key == authKey && param.asAuthToken.tokenType == tokenType) {
return param.asAuthToken;
tokens.push_back(param.asAuthToken);
}
}
return std::nullopt;
return tokens;
}

namespace {
Expand Down Expand Up @@ -324,6 +337,28 @@ bool allows(
return allowsImpl(grants, action, ftn.trackNamespace, std::string_view(ftn.trackName), now);
}

bool allowsAny(
const std::vector<Grants>& grantsList,
Action action,
const TrackNamespace& ns,
std::chrono::system_clock::time_point now
) {
return std::any_of(grantsList.begin(), grantsList.end(), [&](const Grants& grants) {
return allows(grants, action, ns, now);
});
}

bool allowsAny(
const std::vector<Grants>& grantsList,
Action action,
const FullTrackName& ftn,
std::chrono::system_clock::time_point now
) {
return std::any_of(grantsList.begin(), grantsList.end(), [&](const Grants& grants) {
return allows(grants, action, ftn, now);
});
}

const char* toString(AuthError error) {
switch (error) {
case AuthError::Missing:
Expand Down
38 changes: 30 additions & 8 deletions src/auth/Auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,11 @@ class AuthTokenVerifier {
std::unordered_map<std::string, std::size_t> keyIdIndex_;
};

std::optional<moxygen::AuthToken>
findAuthToken(const moxygen::Parameters& params, uint64_t tokenType);
// Returns every AUTHORIZATION_TOKEN parameter matching tokenType, not just the
// first — a message may carry more than one, and a request is authorized if
// any one of them verifies and covers the action (see authorize()).
std::vector<moxygen::AuthToken>
findAuthTokens(const moxygen::Parameters& params, uint64_t tokenType);

// Namespace-level authorization (e.g. PublishNamespace, SubscribeNamespace, setup).
bool allows(
Expand All @@ -106,21 +109,40 @@ bool allows(
std::chrono::system_clock::time_point now = std::chrono::system_clock::now()
);

// True if any element of grantsList allows the action (namespace-level).
bool allowsAny(
const std::vector<Grants>& grantsList,
Action action,
const moxygen::TrackNamespace& ns,
std::chrono::system_clock::time_point now = std::chrono::system_clock::now()
);

// True if any element of grantsList allows the action (track-level).
bool allowsAny(
const std::vector<Grants>& grantsList,
Action action,
const moxygen::FullTrackName& ftn,
std::chrono::system_clock::time_point now = std::chrono::system_clock::now()
);

const char* toString(AuthError error);

// Verifies the setup AUTHORIZATION_TOKEN. Returns null grants when auth is
// disabled; shared grants (possibly empty) to gate the session otherwise.
folly::Expected<std::shared_ptr<const Grants>, AuthError>
// Verifies the setup AUTHORIZATION_TOKEN(s). Returns a null pointer when auth
// is disabled; otherwise a shared vector of every successfully-verified
// setup token's grants (possibly empty), gating the session on whether any
// of them permits Action::ClientSetup.
folly::Expected<std::shared_ptr<const std::vector<Grants>>, AuthError>
authenticateSetup(const AuthTokenVerifier& verifier, const moxygen::Parameters& setupParams);

// Authorizes a request against session grants, or a per-request token when
// allow_request_token_override is set. Returns Unit when permitted.
// Authorizes a request against session grants, or per-request token(s) when
// allow_request_token_override is set. Permitted if any verified request
// token, or any session grant, covers the action. Returns Unit when permitted.
folly::Expected<folly::Unit, AuthError> authorize(
const AuthTokenVerifier& verifier,
Action action,
const moxygen::Parameters& params,
const moxygen::TrackNamespace& ns,
const Grants& sessionGrants,
const std::vector<Grants>& sessionGrants,
std::optional<std::string_view> trackName = std::nullopt
);

Expand Down
8 changes: 4 additions & 4 deletions src/relay/AuthFilters.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class AuthPublisherFilter : public moxygen::Publisher {
AuthPublisherFilter(
std::shared_ptr<moxygen::Publisher> downstream,
std::shared_ptr<const auth::AuthTokenVerifier> verifier,
std::shared_ptr<const auth::Grants> grants,
std::shared_ptr<const std::vector<auth::Grants>> grants,
bool peeringEnabled
)
: downstream_(std::move(downstream)), verifier_(std::move(verifier)),
Expand Down Expand Up @@ -54,7 +54,7 @@ class AuthPublisherFilter : public moxygen::Publisher {
private:
std::shared_ptr<moxygen::Publisher> downstream_;
std::shared_ptr<const auth::AuthTokenVerifier> verifier_;
std::shared_ptr<const auth::Grants> grants_;
std::shared_ptr<const std::vector<auth::Grants>> grants_;
bool peeringEnabled_;
};

Expand All @@ -65,7 +65,7 @@ class AuthSubscriberFilter : public moxygen::Subscriber {
AuthSubscriberFilter(
std::shared_ptr<moxygen::Subscriber> downstream,
std::shared_ptr<const auth::AuthTokenVerifier> verifier,
std::shared_ptr<const auth::Grants> grants
std::shared_ptr<const std::vector<auth::Grants>> grants
)
: downstream_(std::move(downstream)), verifier_(std::move(verifier)),
grants_(std::move(grants)) {}
Expand All @@ -85,7 +85,7 @@ class AuthSubscriberFilter : public moxygen::Subscriber {
private:
std::shared_ptr<moxygen::Subscriber> downstream_;
std::shared_ptr<const auth::AuthTokenVerifier> verifier_;
std::shared_ptr<const auth::Grants> grants_;
std::shared_ptr<const std::vector<auth::Grants>> grants_;
};

} // namespace openmoq::moqx
Loading
Loading