diff --git a/contrib/endpoints/src/api_manager/auth/service_account_token.cc b/contrib/endpoints/src/api_manager/auth/service_account_token.cc index 69f5e8b126e..a0ff6d4cc87 100644 --- a/contrib/endpoints/src/api_manager/auth/service_account_token.cc +++ b/contrib/endpoints/src/api_manager/auth/service_account_token.cc @@ -56,10 +56,20 @@ Status ServiceAccountToken::SetClientAuthSecret(const std::string& secret) { void ServiceAccountToken::SetAudience(JWT_TOKEN_TYPE type, const std::string& audience) { GOOGLE_CHECK(type >= 0 && type < JWT_TOKEN_TYPE_MAX); - jwt_tokens_[type].set_audience(audience); + if (jwt_tokens_[type].audience() != audience) { + jwt_tokens_[type].set_token("", 0); + jwt_tokens_[type].set_audience(audience); + } } const std::string& ServiceAccountToken::GetAuthToken(JWT_TOKEN_TYPE type) { + return GetAuthToken(type, jwt_tokens_[type].audience()); +} + +const std::string& ServiceAccountToken::GetAuthToken( + JWT_TOKEN_TYPE type, const std::string& audience) { + SetAudience(type, audience); + // Uses authentication secret if available. if (!client_auth_secret_.empty()) { GOOGLE_CHECK(type >= 0 && type < JWT_TOKEN_TYPE_MAX); diff --git a/contrib/endpoints/src/api_manager/auth/service_account_token.h b/contrib/endpoints/src/api_manager/auth/service_account_token.h index ef3cabce600..1946e4daaa6 100644 --- a/contrib/endpoints/src/api_manager/auth/service_account_token.h +++ b/contrib/endpoints/src/api_manager/auth/service_account_token.h @@ -79,6 +79,13 @@ class ServiceAccountToken { // Otherwise, use the access token fetched from metadata server. const std::string& GetAuthToken(JWT_TOKEN_TYPE type); + // Gets the auth token to access Google services. This method accepts an + // audience parameter to set when generating JWT token. + // If client auth secret is specified, use it to calcualte JWT token. + // Otherwise, use the access token fetched from metadata server. + const std::string& GetAuthToken(JWT_TOKEN_TYPE type, + const std::string& audience); + private: // Stores base token info. Used for both OAuth and JWT tokens. class TokenInfo { diff --git a/contrib/endpoints/src/api_manager/check_security_rules.cc b/contrib/endpoints/src/api_manager/check_security_rules.cc index 2bb5180e981..c5a2497e894 100644 --- a/contrib/endpoints/src/api_manager/check_security_rules.cc +++ b/contrib/endpoints/src/api_manager/check_security_rules.cc @@ -23,6 +23,9 @@ using ::google::api_manager::auth::GetStringValue; using ::google::api_manager::firebase_rules::FirebaseRequest; using ::google::api_manager::utils::Status; +const char kFirebaseAudience[] = + "https://staging-firebaserules.sandbox.googleapis.com/" + "google.firebase.rules.v1.FirebaseRulesService"; namespace google { namespace api_manager { @@ -77,6 +80,7 @@ class AuthzChecker : public std::enable_shared_from_this { void HttpFetch(const std::string &url, const std::string &method, const std::string &request_body, auth::ServiceAccountToken::JWT_TOKEN_TYPE token_type, + const std::string &audience, std::function continuation); std::shared_ptr GetPtr() { return shared_from_this(); } @@ -107,8 +111,8 @@ void AuthzChecker::Check( auto checker = GetPtr(); HttpFetch(GetReleaseUrl(*context), kHttpGetMethod, "", auth::ServiceAccountToken::JWT_TOKEN_FOR_FIREBASE, - [context, final_continuation, checker](Status status, - std::string &&body) { + kFirebaseAudience, [context, final_continuation, checker]( + Status status, std::string &&body) { std::string ruleset_id; if (status.ok()) { checker->env_->LogDebug( @@ -143,16 +147,17 @@ void AuthzChecker::CallNextRequest( auto checker = GetPtr(); firebase_rules::HttpRequest http_request = request_handler_->GetHttpRequest(); HttpFetch(http_request.url, http_request.method, http_request.body, - http_request.token_type, + http_request.token_type, http_request.audience, [continuation, checker](Status status, std::string &&body) { checker->env_->LogError(std::string("Response Body = ") + body); - if (status.ok()) { + if (status.ok() && !body.empty()) { checker->request_handler_->UpdateResponse(body); checker->CallNextRequest(continuation); } else { - checker->env_->LogError(std::string("Test API failed with ") + - status.ToString()); + checker->env_->LogError( + std::string("Test API failed with ") + + (status.ok() ? "Empty Response" : status.ToString())); status = Status(Code::INTERNAL, kFailedFirebaseTest); continuation(status); } @@ -187,6 +192,7 @@ void AuthzChecker::HttpFetch( const std::string &url, const std::string &method, const std::string &request_body, auth::ServiceAccountToken::JWT_TOKEN_TYPE token_type, + const std::string &audience, std::function continuation) { env_->LogDebug(std::string("Issue HTTP Request to url :") + url + " method : " + method + " body: " + request_body); @@ -201,7 +207,7 @@ void AuthzChecker::HttpFetch( } request->set_method(method).set_url(url).set_auth_token( - sa_token_->GetAuthToken(token_type)); + sa_token_->GetAuthToken(token_type, audience)); if (!request_body.empty()) { request->set_header(kContentType, kApplication).set_body(request_body); diff --git a/contrib/endpoints/src/api_manager/check_security_rules_test.cc b/contrib/endpoints/src/api_manager/check_security_rules_test.cc index bf60d21bd60..cefab67fbab 100644 --- a/contrib/endpoints/src/api_manager/check_security_rules_test.cc +++ b/contrib/endpoints/src/api_manager/check_security_rules_test.cc @@ -43,7 +43,7 @@ using ::google::protobuf::RepeatedPtrField; // Tuple with arg<0> = function name // arg<1> = url, arg<2> = method, arg<3> = body. using FuncTuple = - std::tuple; + std::tuple; using ::google::api_manager::proto::TestRulesetResponse; using FunctionCall = TestRulesetResponse::TestResult::FunctionCall; @@ -157,14 +157,16 @@ static const char kDummyBody[] = R"( "key" : "value" })"; +static const char kDummyAudience[] = "test-audience"; + const char kFirstRequest[] = R"({"testSuite":{"testCases":[{"expectation":"ALLOW","request":{"method":"get","path":"/ListShelves","auth":{"token":{"email":"limin-429@appspot.gserviceaccount.com","email_verified":true,"azp":"limin-429@appspot.gserviceaccount.com","aud":"https://myfirebaseapp.appspot.com","sub":"113424383671131376652","iat":1486575396,"iss":"https://accounts.google.com","exp":1486578996}}}}]}})"; const char kSecondRequest[] = - R"({"testSuite":{"testCases":[{"expectation":"ALLOW","request":{"auth":{"token":{"email":"limin-429@appspot.gserviceaccount.com","azp":"limin-429@appspot.gserviceaccount.com","aud":"https://myfirebaseapp.appspot.com","sub":"113424383671131376652","iss":"https://accounts.google.com","email_verified":true,"iat":1486575396,"exp":1486578996}},"method":"get","path":"/ListShelves"},"functionMocks":[{"function":"f1","args":[{"exactValue":"http://url1"},{"exactValue":"POST"},{"exactValue":{"key":"value"}}],"result":{"value":{"key":"value"}}}]}]}})"; + R"({"testSuite":{"testCases":[{"expectation":"ALLOW","request":{"auth":{"token":{"email":"limin-429@appspot.gserviceaccount.com","azp":"limin-429@appspot.gserviceaccount.com","aud":"https://myfirebaseapp.appspot.com","sub":"113424383671131376652","iss":"https://accounts.google.com","email_verified":true,"iat":1486575396,"exp":1486578996}},"method":"get","path":"/ListShelves"},"functionMocks":[{"function":"f1","args":[{"exactValue":"http://url1"},{"exactValue":"POST"},{"exactValue":{"key":"value"}},{"exactValue":"test-audience"}],"result":{"value":{"key":"value"}}}]}]}})"; const char kThirdRequest[] = - R"({"testSuite":{"testCases":[{"expectation":"ALLOW","request":{"method":"get","path":"/ListShelves","auth":{"token":{"email":"limin-429@appspot.gserviceaccount.com","iat":1486575396,"azp":"limin-429@appspot.gserviceaccount.com","exp":1486578996,"email_verified":true,"sub":"113424383671131376652","aud":"https://myfirebaseapp.appspot.com","iss":"https://accounts.google.com"}}},"functionMocks":[{"function":"f2","args":[{"exactValue":"http://url2"},{"exactValue":"GET"},{"exactValue":{"key":"value"}}],"result":{"value":{"key":"value"}}},{"function":"f3","args":[{"exactValue":"https://url3"},{"exactValue":"GET"},{"exactValue":{"key":"value"}}],"result":{"value":{"key":"value"}}},{"function":"f1","args":[{"exactValue":"http://url1"},{"exactValue":"POST"},{"exactValue":{"key":"value"}}],"result":{"value":{"key":"value"}}}]}]}})"; + R"({"testSuite":{"testCases":[{"expectation":"ALLOW","request":{"method":"get","path":"/ListShelves","auth":{"token":{"email":"limin-429@appspot.gserviceaccount.com","iat":1486575396,"azp":"limin-429@appspot.gserviceaccount.com","exp":1486578996,"email_verified":true,"sub":"113424383671131376652","aud":"https://myfirebaseapp.appspot.com","iss":"https://accounts.google.com"}}},"functionMocks":[{"function":"f2","args":[{"exactValue":"http://url2"},{"exactValue":"GET"},{"exactValue":{"key":"value"}},{"exactValue":"test-audience"}],"result":{"value":{"key":"value"}}},{"function":"f3","args":[{"exactValue":"https://url3"},{"exactValue":"GET"},{"exactValue":{"key":"value"}},{"exactValue":"test-audience"}],"result":{"value":{"key":"value"}}},{"function":"f1","args":[{"exactValue":"http://url1"},{"exactValue":"POST"},{"exactValue":{"key":"value"}},{"exactValue":"test-audience"}],"result":{"value":{"key":"value"}}}]}]}})"; ::google::protobuf::Value ToValue(const std::string &arg) { ::google::protobuf::Value value; @@ -197,7 +199,8 @@ MATCHER_P3(HTTPRequestMatches, url, method, body, "") { } FunctionCall BuildCall(const std::string &name, const std::string &url, - const std::string &method, const std::string &body) { + const std::string &method, const std::string &body, + const std::string &audience) { FunctionCall func_call; func_call.set_function(name); @@ -213,6 +216,10 @@ FunctionCall BuildCall(const std::string &name, const std::string &url, *(func_call.add_args()) = ToValue(body); } + if (!audience.empty()) { + *(func_call.add_args()) = ToValue(audience); + } + return func_call; } @@ -386,6 +393,10 @@ class CheckSecurityRulesTest : public ::testing::Test { Status status = utils::JsonToProto(std::get<3>(http), &body); *(func->add_args()) = body; } + + if (!std::get<4>(http).empty()) { + func->add_args()->set_string_value(std::get<4>(http)); + } } std::string json_str; @@ -526,26 +537,30 @@ class CheckSecurityRulesFunctions : public CheckSecurityRulesTest, InSequence s; ExpectCall(release_url_, "GET", "", kRelease); - ExpectCall( - ruleset_test_url_, "POST", kFirstRequest, - BuildTestRulesetResponse( - false, {std::make_tuple("f1", "http://url1", "POST", kDummyBody)})); + ExpectCall(ruleset_test_url_, "POST", kFirstRequest, + BuildTestRulesetResponse( + false, {std::make_tuple("f1", "http://url1", "POST", + kDummyBody, kDummyAudience)})); ExpectCall("http://url1", "POST", kDummyBody, kDummyBody); - ExpectCall( - ruleset_test_url_, "POST", kSecondRequest, - BuildTestRulesetResponse( - false, {std::make_tuple("f2", "http://url2", "GET", kDummyBody), - std::make_tuple("f3", "https://url3", "GET", kDummyBody), - std::make_tuple("f1", "http://url1", "POST", kDummyBody)})); + ExpectCall(ruleset_test_url_, "POST", kSecondRequest, + BuildTestRulesetResponse( + false, {std::make_tuple("f2", "http://url2", "GET", + kDummyBody, kDummyAudience), + std::make_tuple("f3", "https://url3", "GET", + kDummyBody, kDummyAudience), + std::make_tuple("f1", "http://url1", "POST", + kDummyBody, kDummyAudience)})); ExpectCall("http://url2", "GET", kDummyBody, kDummyBody); ExpectCall("https://url3", "GET", kDummyBody, kDummyBody); ExpectCall(ruleset_test_url_, "POST", kThirdRequest, BuildTestRulesetResponse( - GetParam(), - {std::make_tuple("f2", "http://url2", "GET", kDummyBody), - std::make_tuple("f3", "https://url3", "GET", kDummyBody), - std::make_tuple("f1", "http://url1", "POST", kDummyBody)})); + GetParam(), {std::make_tuple("f2", "http://url2", "GET", + kDummyBody, kDummyAudience), + std::make_tuple("f3", "https://url3", "GET", + kDummyBody, kDummyAudience), + std::make_tuple("f1", "http://url1", "POST", + kDummyBody, kDummyAudience)})); } }; @@ -611,18 +626,19 @@ TEST_P(CheckSecurityRulesBadFunctions, CheckBadFunctionArguments) { }); } -INSTANTIATE_TEST_CASE_P(CheckSecurityRulesBadFunctionArguments, - CheckSecurityRulesBadFunctions, - ::testing::Values( - // Empty function name - std::make_tuple("", "http://url1", "POST", - kDummyBody), - // Argument count less than 2 - std::make_tuple("f1", "", "", ""), - // The url is not set - std::make_tuple("f1", "", "POST", kDummyBody), - // The url is not a http or https protocol - std::make_tuple("f1", "ftp://url1", "BODY", ""))); +INSTANTIATE_TEST_CASE_P( + CheckSecurityRulesBadFunctionArguments, CheckSecurityRulesBadFunctions, + ::testing::Values( + // Empty function name + std::make_tuple("", "http://url1", "POST", kDummyBody, kDummyAudience), + // Argument count less than 3 + std::make_tuple("f1", "http://url1", "", "", kDummyAudience), + // The url is not set + std::make_tuple("f1", "", "POST", kDummyBody, kDummyAudience), + // The url is not a http or https protocol + std::make_tuple("f1", "ftp://url1", "POST", kDummyBody, kDummyAudience), + // The audience is not present + std::make_tuple("f1", "http://url1", "GET", kDummyBody, ""))); } } // namespace api_manager } // namespace google diff --git a/contrib/endpoints/src/api_manager/context/service_context.cc b/contrib/endpoints/src/api_manager/context/service_context.cc index 8e1493d3cfa..92b850481e7 100644 --- a/contrib/endpoints/src/api_manager/context/service_context.cc +++ b/contrib/endpoints/src/api_manager/context/service_context.cc @@ -73,21 +73,6 @@ ServiceContext::ServiceContext(std::unique_ptr env, ->service_control_config() .intermediate_report_min_interval(); } - - service_account_token_.SetAudience( - auth::ServiceAccountToken::JWT_TOKEN_FOR_FIREBASE, kFirebaseAudience); - - if (config_->server_config() && - !config_->server_config() - ->api_check_security_rules_config() - .authorization_service_audience() - .empty()) { - service_account_token_.SetAudience( - auth::ServiceAccountToken::JWT_TOKEN_FOR_AUTHORIZATION_SERVICE, - config_->server_config() - ->api_check_security_rules_config() - .authorization_service_audience()); - } } MethodCallInfo ServiceContext::GetMethodCallInfo( diff --git a/contrib/endpoints/src/api_manager/firebase_rules/firebase_request.cc b/contrib/endpoints/src/api_manager/firebase_rules/firebase_request.cc index 300d521ed1f..7a69cbed47c 100644 --- a/contrib/endpoints/src/api_manager/firebase_rules/firebase_request.cc +++ b/contrib/endpoints/src/api_manager/firebase_rules/firebase_request.cc @@ -49,6 +49,9 @@ const std::string kFirebaseDeleteMethod = "delete"; const std::string kFirebaseUpdateMethod = "update"; const std::string kV1 = "/v1"; const std::string kTestQuery = ":test?alt=json"; +const char kFirebaseAudience[] = + "https://staging-firebaserules.sandbox.googleapis.com/" + "google.firebase.rules.v1.FirebaseRulesService"; void SetProtoValue(const std::string &key, const ::google::protobuf::Value &value, @@ -94,6 +97,8 @@ FirebaseRequest::FirebaseRequest( firebase_http_request_.method = kHttpPostMethod; firebase_http_request_.token_type = auth::ServiceAccountToken::JWT_TOKEN_FOR_FIREBASE; + firebase_http_request_.audience = kFirebaseAudience; + external_http_request_.token_type = auth::ServiceAccountToken::JWT_TOKEN_FOR_AUTHORIZATION_SERVICE; @@ -305,6 +310,8 @@ Status FirebaseRequest::SetNextRequest() { auto call = *func_call_iter_; external_http_request_.url = call.args(0).string_value(); external_http_request_.method = call.args(1).string_value(); + external_http_request_.audience = + call.args(call.args_size() - 1).string_value(); std::string body; status = utils::ProtoToJson(call.args(2), &body, utils::JsonOptions::DEFAULT); @@ -334,9 +341,9 @@ Status FirebaseRequest::CheckFuncCallArgs(const FunctionCall &func) { // We only support functions that call with three argument: HTTP URL, HTTP // method and body. The body can be empty - if (func.args_size() < 2 || func.args_size() > 3) { + if (func.args_size() < 3 || func.args_size() > 4) { std::ostringstream os; - os << func.function() << " Require 2 or 3 arguments. But has " + os << func.function() << " Require 3 or 4 arguments. But has " << func.args_size(); return Status(Code::INVALID_ARGUMENT, os.str()); } @@ -348,6 +355,14 @@ Status FirebaseRequest::CheckFuncCallArgs(const FunctionCall &func) { std::string(func.function() + " Arguments 1 and 2 should be strings")); } + if (func.args(func.args_size() - 1).kind_case() != + google::protobuf::Value::kStringValue) { + return Status( + Code::INVALID_ARGUMENT, + std::string(func.function() + "The last argument should be a string" + "that specifies audience")); + } + if (!utils::IsHttpRequest(func.args(0).string_value())) { return Status( Code::INVALID_ARGUMENT, diff --git a/contrib/endpoints/src/api_manager/firebase_rules/firebase_request.h b/contrib/endpoints/src/api_manager/firebase_rules/firebase_request.h index ee26b77e646..1a9a22ea699 100644 --- a/contrib/endpoints/src/api_manager/firebase_rules/firebase_request.h +++ b/contrib/endpoints/src/api_manager/firebase_rules/firebase_request.h @@ -74,6 +74,7 @@ struct HttpRequest { std::string method; std::string body; auth::ServiceAccountToken::JWT_TOKEN_TYPE token_type; + std::string audience; }; // A FirebaseRequest object understands the various http requests that need diff --git a/contrib/endpoints/src/api_manager/proto/server_config.proto b/contrib/endpoints/src/api_manager/proto/server_config.proto index 5e26613f140..1ec8eefe5d5 100644 --- a/contrib/endpoints/src/api_manager/proto/server_config.proto +++ b/contrib/endpoints/src/api_manager/proto/server_config.proto @@ -164,7 +164,6 @@ message ApiAuthenticationConfig { message ApiCheckSecurityRulesConfig { // Firebase server to use. string firebase_server = 1; - string authorization_service_audience = 2; } message Experimental {