Commit 803bdf13 authored by Aleksandr Konstantinov's avatar Aleksandr Konstantinov

Merge branch 'scitokens' into 'master'

Tokens

See merge request nordugrid/arc!963
parents f76348b0 7259b08d
#include "../../../src/hed/libs/otokens/openid_metadata.h"
......@@ -434,7 +434,7 @@ namespace Arc {
}
MCC_Status ClientHTTP::process(const std::string& method,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
PayloadRawInterface *request,
HTTPClientInfo *info,
PayloadRawInterface **response) {
......@@ -452,7 +452,7 @@ namespace Arc {
MCC_Status ClientHTTP::process(const std::string& method,
const std::string& path,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
PayloadRawInterface *request,
HTTPClientInfo *info,
PayloadRawInterface **response) {
......@@ -471,7 +471,7 @@ namespace Arc {
MCC_Status ClientHTTP::process(const std::string& method,
const std::string& path,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
uint64_t range_start, uint64_t range_end,
PayloadRawInterface *request,
HTTPClientInfo *info,
......@@ -502,7 +502,7 @@ namespace Arc {
MCC_Status ClientHTTP::process(const std::string& method,
const std::string& path,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
uint64_t range_start, uint64_t range_end,
MessagePayload *request,
HTTPClientInfo *info,
......@@ -668,7 +668,7 @@ namespace Arc {
}
ClientHTTPAttributes::ClientHTTPAttributes(const std::string& method,
std::multimap<std::string, std::string>& attributes):
std::multimap<std::string, std::string> const& attributes):
method_(method),path_(default_path_),attributes_(attributes),
range_start_(0),range_end_(UINT64_MAX) {
}
......@@ -681,7 +681,7 @@ namespace Arc {
ClientHTTPAttributes::ClientHTTPAttributes(const std::string& method,
const std::string& path,
std::multimap<std::string, std::string>& attributes):
std::multimap<std::string, std::string> const& attributes):
method_(method),path_(path),attributes_(attributes),
range_start_(0),range_end_(UINT64_MAX) {
}
......@@ -695,7 +695,7 @@ namespace Arc {
ClientHTTPAttributes::ClientHTTPAttributes(const std::string& method,
const std::string& path,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
uint64_t range_start, uint64_t range_end):
method_(method),path_(path),attributes_(attributes),
range_start_(range_start),range_end_(range_end) {
......@@ -728,7 +728,7 @@ namespace Arc {
return process(std::multimap<std::string, std::string>(), request, response);
}
MCC_Status ClientSOAP::process(const std::multimap<std::string, std::string> &http_attr,
MCC_Status ClientSOAP::process(std::multimap<std::string, std::string> const& http_attr,
PayloadSOAP *request,
PayloadSOAP **response) {
*response = NULL;
......@@ -763,7 +763,7 @@ namespace Arc {
return process(std::multimap<std::string, std::string>(), action, request, response);
}
MCC_Status ClientSOAP::process(const std::multimap<std::string, std::string> &http_attr,
MCC_Status ClientSOAP::process(std::multimap<std::string, std::string> const& http_attr,
const std::string& action,
PayloadSOAP *request,
PayloadSOAP **response) {
......
......@@ -153,21 +153,21 @@ namespace Arc {
public:
ClientHTTPAttributes(const std::string& method);
ClientHTTPAttributes(const std::string& method,
std::multimap<std::string, std::string>& attributes);
std::multimap<std::string, std::string> const& attributes);
ClientHTTPAttributes(const std::string& method, const std::string& path);
ClientHTTPAttributes(const std::string& method, const std::string& path,
std::multimap<std::string, std::string>& attributes);
std::multimap<std::string, std::string> const& attributes);
ClientHTTPAttributes(const std::string& method, const std::string& path,
uint64_t range_start, uint64_t range_end);
ClientHTTPAttributes(const std::string& method, const std::string& path,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
uint64_t range_start, uint64_t range_end);
protected:
const std::string default_path_;
std::multimap<std::string, std::string> default_attributes_;
const std::string& method_;
const std::string& path_;
std::multimap<std::string, std::string>& attributes_;
const std::string method_;
const std::string path_;
std::multimap<std::string, std::string> attributes_;
uint64_t range_start_;
uint64_t range_end_;
};
......@@ -186,14 +186,14 @@ namespace Arc {
MCC_Status process(const std::string& method, PayloadRawInterface *request,
HTTPClientInfo *info, PayloadRawInterface **response);
MCC_Status process(const std::string& method,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
PayloadRawInterface *request,
HTTPClientInfo *info, PayloadRawInterface **response);
MCC_Status process(const std::string& method, const std::string& path,
PayloadRawInterface *request,
HTTPClientInfo *info, PayloadRawInterface **response);
MCC_Status process(const std::string& method, const std::string& path,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
PayloadRawInterface *request,
HTTPClientInfo *info, PayloadRawInterface **response);
MCC_Status process(const std::string& method, const std::string& path,
......@@ -201,7 +201,7 @@ namespace Arc {
PayloadRawInterface *request,
HTTPClientInfo *info, PayloadRawInterface **response);
MCC_Status process(const std::string& method, const std::string& path,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
uint64_t range_start, uint64_t range_end,
PayloadRawInterface *request,
HTTPClientInfo *info, PayloadRawInterface **response);
......@@ -234,7 +234,7 @@ namespace Arc {
TCPSec sec;
bool closed;
MCC_Status process(const std::string& method, const std::string& path,
std::multimap<std::string, std::string>& attributes,
std::multimap<std::string, std::string> const& attributes,
uint64_t range_start, uint64_t range_end,
MessagePayload *request,
HTTPClientInfo *info, MessagePayload **response);
......@@ -254,12 +254,12 @@ namespace Arc {
/** Send SOAP request and receive response. */
MCC_Status process(PayloadSOAP *request, PayloadSOAP **response);
/** Send SOAP request + additional HTTP header attributes and receive response. */
MCC_Status process(const std::multimap<std::string, std::string> &http_attr, PayloadSOAP *request, PayloadSOAP **response);
MCC_Status process(std::multimap<std::string, std::string> const& http_attr, PayloadSOAP *request, PayloadSOAP **response);
/** Send SOAP request with specified SOAP action and receive response. */
MCC_Status process(const std::string& action, PayloadSOAP *request,
PayloadSOAP **response);
/** Send SOAP request with specified SOAP action + additional HTTP header attributes and receive response. */
MCC_Status process(const std::multimap<std::string, std::string> &http_attr, const std::string& action, PayloadSOAP *request,
MCC_Status process(std::multimap<std::string, std::string> const& http_attr, const std::string& action, PayloadSOAP *request,
PayloadSOAP **response);
/** Returns entry point to SOAP MCC in configured chain.
To initialize entry point Load() method must be called. */
......
......@@ -28,7 +28,7 @@ namespace Arc {
char const* const JWSE::HeaderNameAlgorithm = "alg";
char const* const JWSE::HeaderNameEncryption = "enc";
JWSE::JWSE(): valid_(false), header_(NULL, &cJSON_Delete) {
JWSE::JWSE(): valid_(false), header_(NULL, &cJSON_Delete), keyOrigin_(NoKey) {
OpenSSLInit();
header_ = cJSON_CreateObject();
......@@ -43,7 +43,7 @@ namespace Arc {
valid_ = true;
}
JWSE::JWSE(std::string const& jwseCompact): valid_(false), header_(NULL, &cJSON_Delete) {
JWSE::JWSE(std::string const& jwseCompact): valid_(false), header_(NULL, &cJSON_Delete), keyOrigin_(NoKey) {
OpenSSLInit();
(void)Input(jwseCompact);
......@@ -129,8 +129,11 @@ namespace Arc {
std::string signature = Base64::decodeURLSafe(signatureStart, signatureEnd-signatureStart);
bool verifyResult = false;
logger_.msg(DEBUG, "JWSE::Input: JWS: signature algorthm: %s", algObject->valuestring);
signAlg_ = algObject->valuestring;
if(strcmp(algObject->valuestring, "none") == 0) {
verifyResult = signature.empty(); // expecting empty signature if no protection is requested
keyOrigin_ = NoKey;
signAlg_.clear();
} else if(strcmp(algObject->valuestring, "HS256") == 0) {
verifyResult = VerifyHMAC("SHA256", joseStart, payloadEnd-joseStart,
reinterpret_cast<unsigned char const*>(signature.c_str()), signature.length());
......
......@@ -262,7 +262,7 @@ namespace Arc {
bool JWSE::ExtractPublicKey() const {
key_.Release();
// So far we are going to support only embedded keys - jwk and x5c
// So far we are going to support only embedded keys jwk and x5c and external kid.
cJSON* x5cObject = cJSON_GetObjectItem(header_.Ptr(), HeaderNameX509CertChain);
cJSON* jwkObject = cJSON_GetObjectItem(header_.Ptr(), HeaderNameJSONWebKey);
cJSON* kidObject = cJSON_GetObjectItem(header_.Ptr(), HeaderNameJSONWebKeyId);
......@@ -270,6 +270,7 @@ namespace Arc {
AutoPointer<JWSEKeyHolder> key(new JWSEKeyHolder());
logger_.msg(DEBUG, "JWSE::ExtractPublicKey: x5c key");
if(x5cParse(x5cObject, *key)) {
keyOrigin_ = EmbeddedKey;
key_ = key;
return true;
}
......@@ -277,6 +278,7 @@ namespace Arc {
AutoPointer<JWSEKeyHolder> key(new JWSEKeyHolder());
logger_.msg(DEBUG, "JWSE::ExtractPublicKey: jwk key");
if(jwkParse(jwkObject, *key)) {
keyOrigin_ = EmbeddedKey;
key_ = key;
return true;
}
......@@ -288,11 +290,16 @@ namespace Arc {
cJSON* issuerObj = cJSON_GetObjectItem(content_.Ptr(), ClaimNameIssuer);
if(!issuerObj || (issuerObj->type != cJSON_String))
return false;
bool keyProtocolSafe = true;
if(strncasecmp("https:", issuerObj->valuestring, 6) != 0) keyProtocolSafe = false;
OpenIDMetadata serviceMetadata;
OpenIDMetadataFetcher metadataFetcher(issuerObj->valuestring);
if(!metadataFetcher.Fetch(serviceMetadata))
return false;
char const * jwksUri = serviceMetadata.JWKSURI();
if(strncasecmp("https:", jwksUri, 6) != 0) keyProtocolSafe = false;
logger_.msg(DEBUG, "JWSE::ExtractPublicKey: fetching jwl key from %s", jwksUri);
JWSEKeyFetcher keyFetcher(jwksUri);
JWSEKeyHolderList keys;
......@@ -300,6 +307,7 @@ namespace Arc {
return false;
for(JWSEKeyHolderList::iterator keyIt = keys.begin(); keyIt != keys.end(); ++keyIt) {
if(strcmp(kidObject->valuestring, (*keyIt)->Id()) == 0) {
keyOrigin_ = keyProtocolSafe ? ExternalSafeKey : ExternalUnsafeKey;
key_ = *keyIt;
return true;
}
......
......@@ -18,6 +18,8 @@
namespace Arc {
Logger OpenIDMetadata::logger_(Logger::getRootLogger(), "OpenIDMetadata");
Logger OpenIDMetadataFetcher::logger_(Logger::getRootLogger(), "OpenIDMetadataFetcher");
Logger OpenIDTokenFetcher::logger_(Logger::getRootLogger(), "OpenIDTokenFetcher");
OpenIDMetadata::OpenIDMetadata(): valid_(false), metadata_(NULL, &cJSON_Delete) {
metadata_ = cJSON_CreateObject();
......@@ -365,29 +367,29 @@ namespace Arc {
if(!grant.empty()) {
if(!tokenReq.empty()) tokenReq.append("&");
tokenReq.append("grant_type=");
tokenReq.append(Base64::encodeURLSafe(grant));
tokenReq.append(uri_encode(grant, true));
} else if ((!client_id_.empty()) || (!client_secret_.empty())) {
if(!tokenReq.empty()) tokenReq.append("&");
tokenReq.append("grant_type=");
tokenReq.append(Base64::encodeURLSafe("client_credentials"));
tokenReq.append(uri_encode("client_credentials", true));
}
if(!subject.empty()) {
if(!tokenReq.empty()) tokenReq.append("&");
tokenReq.append("subject_token=");
tokenReq.append(Base64::encodeURLSafe(subject));
tokenReq.append(uri_encode(subject, true));
}
if(!audience.empty()) {
if(!tokenReq.empty()) tokenReq.append("&");
tokenReq.append("audience=");
tokenReq.append(Base64::encodeURLSafe(join(audience, " ")));
tokenReq.append(uri_encode(join(audience, " "), true));
}
if(!scope.empty()) {
if(!tokenReq.empty()) tokenReq.append("&");
tokenReq.append("scope=");
tokenReq.append(Base64::encodeURLSafe(join(scope, " ")));
tokenReq.append(uri_encode(join(scope, " "), true));
}
if(!tokenReq.empty()) {
......@@ -408,8 +410,12 @@ namespace Arc {
if(!status)
return false;
if(info.code != 200)
if(info.code != 200) {
logger_.msg(DEBUG, "Fetch: response code: %u %s", info.code, info.reason);
char const * responseContent = response ? response->Content() : NULL;
if(responseContent) logger_.msg(DEBUG, "Fetch: response body: %s", responseContent);
return false;
}
if(response) {
char const * responseContent = response->Content();
......
#include <string>
#include <arc/Utils.h>
#include <arc/URL.h>
#include <arc/Logger.h>
#include <arc/communication/ClientInterface.h>
struct cJSON;
......@@ -162,6 +164,7 @@ namespace Arc {
private:
URL url_;
ClientHTTP client_;
static Logger logger_;
};
class OpenIDTokenFetcher {
......@@ -178,6 +181,7 @@ namespace Arc {
std::string client_id_;
std::string client_secret_;
ClientHTTP client_;
static Logger logger_;
};
......
......@@ -25,6 +25,13 @@ namespace Arc {
static char const * const HeaderNameAlgorithm;
static char const * const HeaderNameEncryption;
enum KeyOrigin {
NoKey, //< There was no key used for validation
EmbeddedKey, //< Signature was validated using embedded key
ExternalUnsafeKey, //< Signature was validated using key obtained through HTTP protocol
ExternalSafeKey, //< Signature was validated using key obtained through HTTPS protocol
};
//! Parse token available as simple string.
//! Mostly to be used for tokens embedded into something
//! like HTTP header.
......@@ -79,6 +86,12 @@ namespace Arc {
//! Parses passed Token and stores collected information in this object.
bool Input(std::string const& jwseCompact);
//! Returns information about how key used to validate signature is obtained.
KeyOrigin InputKeyOrigin() const { return keyOrigin_; };
//! Returns algorithm which was used to sign token (empty if no signature).
char const * SignatureAlgoritm() const { return signAlg_.c_str(); }
//! Serializes stored Token into string container.
bool Output(std::string& jwseCompact) const;
......@@ -95,6 +108,10 @@ namespace Arc {
mutable AutoPointer<JWSEKeyHolder> key_;
mutable KeyOrigin keyOrigin_;
mutable std::string signAlg_;
mutable AutoPointer<cJSON> content_;
void Cleanup();
......
......@@ -75,6 +75,11 @@ OTokensSecAttr::OTokensSecAttr(Arc::Message* msg):valid_(false) {
token_.erase(0, sizeof(tokenid)-1);
logger.msg(DEBUG, "OTokens: Attr: token: bearer: %s", token_);
valid_ = jwse_.Input(token_);
if(valid_) {
// Additionally require signature protected by safely obtained key.
// So far only accepting keys fetched from issuer through https.
valid_ = (jwse_.InputKeyOrigin() == JWSE::ExternalSafeKey);
};
};
};
};
......@@ -93,6 +98,8 @@ std::string OTokensSecAttr::get(const std::string& id) const {
std::list<std::string> OTokensSecAttr::getAll(const std::string& id) const {
std::list<std::string> items;
if(!valid_)
return items;
if(id == "") { // whole token requested
items.push_back(token_);
return items;
......
......@@ -68,6 +68,7 @@ libarex_la_LIBADD = \
$(top_builddir)/src/hed/libs/data/libarcdata.la \
$(top_builddir)/src/hed/libs/message/libarcmessage.la \
$(top_builddir)/src/hed/libs/loader/libarcloader.la \
$(top_builddir)/src/hed/libs/otokens/libarcotokens.la
$(top_builddir)/src/hed/libs/common/libarccommon.la
libarex_la_LDFLAGS = -no-undefined -avoid-version -module $(DBCXX_LIBS)
......
......@@ -22,6 +22,7 @@
#include <arc/StringConv.h>
#include <arc/FileUtils.h>
#include <arc/Utils.h>
#include <arc/otokens/openid_metadata.h>
#include "job.h"
#include "grid-manager/log/JobLog.h"
#include "grid-manager/log/JobsMetrics.h"
......@@ -436,6 +437,41 @@ ARexConfigContext* ARexService::get_configuration(Arc::Message& inmsg) {
// Try tokens if TLS has no information about user identity
logger_.msg(Arc::ERROR, "TLS provides no identity, going for OTokens");
grid_name = inmsg.Attributes()->get("OTOKENS:IDENTITYDN");
/*
Below is an example on how obtained token can be exchanged.
Arc::SecAttr* sattr = inmsg.Auth()->get("OTOKENS");
if(!sattr) sattr = inmsg.AuthContext()->get("OTOKENS");
if(sattr) {
std::string token = sattr->get("");
if(!token.empty()) {
Arc::OpenIDMetadata tokenMetadata;
Arc::OpenIDMetadataFetcher metaFetcher(sattr->get("iss").c_str());
if(metaFetcher.Fetch(tokenMetadata)) {
char const * tokenEndpointUrl = tokenMetadata.TokenEndpoint();
if(tokenEndpointUrl) {
Arc::OpenIDTokenFetcher tokenFetcher(tokenEndpointUrl,
"c85e84e8-c9ea-4ecc-8123-070df2c10e0e",
"dRnakcoaT-9YA6T1LzeLAqeEu7jLBxeTWFyQMbJ6BWZonjEcE060-dn8EWAfpZmPq3x7oTjUnu6mamYylBaNhw");
std::list<std::string> scopes;
scopes.push_back("storage.read:/");
scopes.push_back("storage.create:/");
std::list<std::string> audiences;
audiences.push_back("se1.example");
audiences.push_back("se2.example");
Arc::OpenIDTokenFetcher::TokenList tokens;
if(tokenFetcher.Fetch("urn:ietf:params:oauth:grant-type:token-exchange", token, scopes, audiences, tokens)) {
for(auto const & token : tokens) {
logger_.msg(Arc::ERROR, "Token response: %s : %s", token.first, token.second);
};
} else logger_.msg(Arc::ERROR, "Failed to fetch token");
} else logger_.msg(Arc::ERROR, "Token metadata contains no token endpoint");;
} else logger_.msg(Arc::ERROR, "Failed to fetch token metadata");
} else logger_.msg(Arc::ERROR, "There is no token in sec attr");
} else logger_.msg(Arc::ERROR, "There is no otoken sec attr");
*/
};
std::string endpoint = endpoint_;
if(endpoint.empty()) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment