Skip to content
Open
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
23 changes: 17 additions & 6 deletions include/miniocpp/providers.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ static constexpr unsigned MAX_DURATION_SECONDS = 60 * 60 * 24 * 7;
struct Jwt {
std::string token;
unsigned int expiry = 0;
std::string access_token;
std::string refresh_token;

Jwt() = default;
explicit Jwt(std::string token, unsigned int expiry)
Expand Down Expand Up @@ -148,7 +150,8 @@ class AssumeRoleProvider : public Provider {
std::string policy = {}, std::string region = {},
std::string role_arn = {},
std::string role_session_name = {},
std::string external_id = {});
std::string external_id = {},
std::string token_revoke_type = {});

virtual ~AssumeRoleProvider();

Expand All @@ -163,13 +166,15 @@ class WebIdentityClientGrantsProvider : public Provider {
std::string policy_;
std::string role_arn_;
std::string role_session_name_;
std::string token_revoke_type_;

public:
WebIdentityClientGrantsProvider(JwtFunction jwtfunc, http::Url sts_endpoint,
unsigned int duration_seconds = 0,
std::string policy = {},
std::string role_arn = {},
std::string role_session_name = {});
std::string role_session_name = {},
std::string token_revoke_type = {});

virtual ~WebIdentityClientGrantsProvider();

Expand All @@ -185,7 +190,8 @@ class ClientGrantsProvider : public WebIdentityClientGrantsProvider {
ClientGrantsProvider(JwtFunction jwtfunc, http::Url sts_endpoint,
unsigned int duration_seconds = 0,
std::string policy = {}, std::string role_arn = {},
std::string role_session_name = {});
std::string role_session_name = {},
std::string token_revoke_type = {});

virtual ~ClientGrantsProvider();

Expand All @@ -197,7 +203,8 @@ class WebIdentityProvider : public WebIdentityClientGrantsProvider {
WebIdentityProvider(JwtFunction jwtfunc, http::Url sts_endpoint,
unsigned int duration_seconds = 0,
std::string policy = {}, std::string role_arn = {},
std::string role_session_name = {});
std::string role_session_name = {},
std::string token_revoke_type = {});

virtual ~WebIdentityProvider();

Expand Down Expand Up @@ -231,7 +238,10 @@ class LdapIdentityProvider : public Provider {

public:
LdapIdentityProvider(http::Url sts_endpoint, std::string ldap_username,
std::string ldap_password);
std::string ldap_password, std::string policy = {},
unsigned int duration_seconds = 0,
std::string token_revoke_type = {},
std::string config_name = {});

virtual ~LdapIdentityProvider();

Expand All @@ -249,7 +259,8 @@ struct CertificateIdentityProvider : public Provider {
CertificateIdentityProvider(http::Url sts_endpoint, std::string key_file,
std::string cert_file,
std::string ssl_cert_file = {},
unsigned int duration_seconds = 0);
unsigned int duration_seconds = 0,
std::string token_revoke_type = {});

virtual ~CertificateIdentityProvider();

Expand Down
70 changes: 55 additions & 15 deletions src/providers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,9 @@ AssumeRoleProvider::AssumeRoleProvider(
http::Url sts_endpoint, std::string access_key, std::string secret_key,
unsigned int duration_seconds, std::string policy, std::string region,
std::string role_arn, std::string role_session_name,
std::string external_id) {
std::string external_id, std::string token_revoke_type) {
this->sts_endpoint_ = sts_endpoint;
if (this->sts_endpoint_.path.empty()) this->sts_endpoint_.path = "/";
this->access_key_ = access_key;
this->secret_key_ = secret_key;
this->region_ = region;
Expand All @@ -229,6 +230,9 @@ AssumeRoleProvider::AssumeRoleProvider(
}
if (!policy.empty()) map.Add("Policy", policy);
if (!external_id.empty()) map.Add("ExternalId", external_id);
if (!token_revoke_type.empty()) {
map.Add("TokenRevokeType", token_revoke_type);
}

this->body_ = map.ToQueryString();
this->content_sha256_ = utils::Sha256Hash(body_);
Expand Down Expand Up @@ -259,7 +263,8 @@ Credentials AssumeRoleProvider::Fetch() {
if (!resp) {
creds_ = Credentials{resp.Error()};
} else {
auto parse_res = Credentials::ParseXML(resp.body, "AssumeRoleResult");
auto parse_res =
Credentials::ParseXML(resp.body, "AssumeRoleResponse/AssumeRoleResult");
if (parse_res) {
creds_ = std::move(*parse_res);
} else {
Expand All @@ -272,13 +277,15 @@ Credentials AssumeRoleProvider::Fetch() {

WebIdentityClientGrantsProvider::WebIdentityClientGrantsProvider(
JwtFunction jwtfunc, http::Url sts_endpoint, unsigned int duration_seconds,
std::string policy, std::string role_arn, std::string role_session_name) {
std::string policy, std::string role_arn, std::string role_session_name,
std::string token_revoke_type) {
this->jwtfunc_ = jwtfunc;
this->sts_endpoint_ = sts_endpoint;
this->duration_seconds_ = duration_seconds;
this->policy_ = policy;
this->role_arn_ = role_arn;
this->role_session_name_ = role_session_name;
this->token_revoke_type_ = token_revoke_type;
}

WebIdentityClientGrantsProvider::~WebIdentityClientGrantsProvider() {}
Expand All @@ -304,10 +311,19 @@ Credentials WebIdentityClientGrantsProvider::Fetch() {
map.Add("DurationSeconds", std::to_string(duration_seconds));
}
if (!policy_.empty()) map.Add("Policy", policy_);
if (!token_revoke_type_.empty()) {
map.Add("TokenRevokeType", token_revoke_type_);
}

if (IsWebIdentity()) {
map.Add("Action", "AssumeRoleWithWebIdentity");
map.Add("WebIdentityToken", jwt.token);
if (!jwt.access_token.empty()) {
map.Add("WebIdentityAccessToken", jwt.access_token);
}
if (!jwt.refresh_token.empty()) {
map.Add("WebIdentityRefreshToken", jwt.refresh_token);
}
if (!role_arn_.empty()) {
map.Add("RoleArn", role_arn_);
if (!role_session_name_.empty()) {
Expand All @@ -329,8 +345,10 @@ Credentials WebIdentityClientGrantsProvider::Fetch() {
creds_ = Credentials{resp.Error()};
} else {
auto parse_res = Credentials::ParseXML(
resp.body, IsWebIdentity() ? "AssumeRoleWithWebIdentityResult"
: "AssumeRoleWithClientGrantsResult");
resp.body, IsWebIdentity() ? "AssumeRoleWithWebIdentityResponse/"
"AssumeRoleWithWebIdentityResult"
: "AssumeRoleWithClientGrantsResponse/"
"AssumeRoleWithClientGrantsResult");
if (parse_res) {
creds_ = std::move(*parse_res);
} else {
Expand All @@ -342,19 +360,23 @@ Credentials WebIdentityClientGrantsProvider::Fetch() {

ClientGrantsProvider::ClientGrantsProvider(
JwtFunction jwtfunc, http::Url sts_endpoint, unsigned int duration_seconds,
std::string policy, std::string role_arn, std::string role_session_name)
std::string policy, std::string role_arn, std::string role_session_name,
std::string token_revoke_type)
: WebIdentityClientGrantsProvider(jwtfunc, sts_endpoint, duration_seconds,
policy, role_arn, role_session_name) {}
policy, role_arn, role_session_name,
token_revoke_type) {}

ClientGrantsProvider::~ClientGrantsProvider() {}

bool ClientGrantsProvider::IsWebIdentity() const { return false; }

WebIdentityProvider::WebIdentityProvider(
JwtFunction jwtfunc, http::Url sts_endpoint, unsigned int duration_seconds,
std::string policy, std::string role_arn, std::string role_session_name)
std::string policy, std::string role_arn, std::string role_session_name,
std::string token_revoke_type)
: WebIdentityClientGrantsProvider(jwtfunc, sts_endpoint, duration_seconds,
policy, role_arn, role_session_name) {}
policy, role_arn, role_session_name,
token_revoke_type) {}

WebIdentityProvider::~WebIdentityProvider() {}

Expand Down Expand Up @@ -477,13 +499,25 @@ error::Error IamAwsProvider::getRoleName(std::string& role_name,

LdapIdentityProvider::LdapIdentityProvider(http::Url sts_endpoint,
std::string ldap_username,
std::string ldap_password) {
std::string ldap_password,
std::string policy,
unsigned int duration_seconds,
std::string token_revoke_type,
std::string config_name) {
this->sts_endpoint_ = sts_endpoint;
utils::Multimap map;
map.Add("Action", "AssumeRoleWithLDAPIdentity");
map.Add("Version", "2011-06-15");
map.Add("LDAPUsername", ldap_username);
map.Add("LDAPPassword", ldap_password);
if (!policy.empty()) map.Add("Policy", policy);
if (duration_seconds > 0) {
map.Add("DurationSeconds", std::to_string(duration_seconds));
}
if (!token_revoke_type.empty()) {
map.Add("TokenRevokeType", token_revoke_type);
}
if (!config_name.empty()) map.Add("ConfigName", config_name);
this->sts_endpoint_.query_string = map.ToQueryString();
}

Expand All @@ -496,8 +530,9 @@ Credentials LdapIdentityProvider::Fetch() {
http::Response resp = req.Execute();
if (!resp) return Credentials{resp.Error()};

auto parse_res =
Credentials::ParseXML(resp.body, "AssumeRoleWithLDAPIdentityResult");
auto parse_res = Credentials::ParseXML(
resp.body,
"AssumeRoleWithLDAPIdentityResponse/AssumeRoleWithLDAPIdentityResult");
if (parse_res) {
creds_ = std::move(*parse_res);
} else {
Expand All @@ -508,7 +543,8 @@ Credentials LdapIdentityProvider::Fetch() {

CertificateIdentityProvider::CertificateIdentityProvider(
http::Url sts_endpoint, std::string key_file, std::string cert_file,
std::string ssl_cert_file, unsigned int duration_seconds) {
std::string ssl_cert_file, unsigned int duration_seconds,
std::string token_revoke_type) {
if (!sts_endpoint.https) {
this->err_ = error::Error("sts endpoint scheme must be HTTPS");
return;
Expand All @@ -528,6 +564,9 @@ CertificateIdentityProvider::CertificateIdentityProvider(
map.Add("Action", "AssumeRoleWithCertificate");
map.Add("Version", "2011-06-15");
map.Add("DurationSeconds", std::to_string(expiry));
if (!token_revoke_type.empty()) {
map.Add("TokenRevokeType", token_revoke_type);
}

sts_endpoint_ = sts_endpoint;
sts_endpoint_.query_string = map.ToQueryString();
Expand All @@ -551,8 +590,9 @@ Credentials CertificateIdentityProvider::Fetch() {
http::Response resp = req.Execute();
if (!resp) return Credentials{resp.Error()};

auto parse_res =
Credentials::ParseXML(resp.body, "AssumeRoleWithCertificateResult");
auto parse_res = Credentials::ParseXML(
resp.body,
"AssumeRoleWithCertificateResponse/AssumeRoleWithCertificateResult");
if (parse_res) {
creds_ = std::move(*parse_res);
} else {
Expand Down
38 changes: 38 additions & 0 deletions tests/tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,43 @@ class Tests {
RemoveObject(bucket_name_, object_name);
}
} // TestAsyncOperations

// Issue #205 regression: a failed AssumeRoleProvider::Fetch() leaves
// access_key/secret_key/session_token empty. The failure must be surfaced
// via creds.err instead of silently printing empty fields; on success all
// three temporary credentials must be non-empty.
void AssumeRoleProvider() {
std::cout << "AssumeRoleProvider()" << std::endl;

const minio::s3::BaseUrl& base_url = client_.GetBaseUrl();
minio::http::Url sts_endpoint(base_url.https, base_url.host, base_url.port);

std::string access_key;
std::string secret_key;
minio::utils::GetEnv(access_key, "ACCESS_KEY");
minio::utils::GetEnv(secret_key, "SECRET_KEY");

std::string region =
base_url.region.empty() ? "us-east-1" : base_url.region;
minio::creds::AssumeRoleProvider provider(sts_endpoint, access_key,
secret_key, 900, "", region);
minio::creds::Credentials creds = provider.Fetch();

if (!creds.err.String().empty()) {
// Failure path (issue #205): empty credential fields must be
// accompanied by a diagnosable error.
throw std::runtime_error(
"AssumeRoleProvider(): Fetch() failed with an error" +
creds.err.String());
return;
}

if (creds.access_key.empty() || creds.secret_key.empty() ||
creds.session_token.empty()) {
throw std::runtime_error(
"AssumeRoleProvider(): temporary credentials are empty");
}
}
}; // class Tests

int main(int /*argc*/, char* /*argv*/[]) {
Expand Down Expand Up @@ -1596,6 +1633,7 @@ int main(int /*argc*/, char* /*argv*/[]) {
tests.SelectObjectContent();
tests.ListenBucketNotification();
tests.TestAsyncOperations();
tests.AssumeRoleProvider();

return EXIT_SUCCESS;
}
Loading