blob: f2591a1ea5e2f780a89016018e0e4f2540065c77 [file] [log] [blame]
/*
*
* Copyright 2016 gRPC authors.
*
* 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.
*
*/
#include <grpc/support/port_platform.h>
#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
#include <inttypes.h>
#include <string.h>
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/surface/api_trace.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
void grpc_service_account_jwt_access_credentials::reset_cache() {
GRPC_MDELEM_UNREF(cached_.jwt_md);
cached_.jwt_md = GRPC_MDNULL;
if (cached_.service_url != nullptr) {
gpr_free(cached_.service_url);
cached_.service_url = nullptr;
}
cached_.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
}
grpc_service_account_jwt_access_credentials::
~grpc_service_account_jwt_access_credentials() {
grpc_auth_json_key_destruct(&key_);
reset_cache();
gpr_mu_destroy(&cache_mu_);
}
bool grpc_service_account_jwt_access_credentials::get_request_metadata(
grpc_polling_entity* pollent, grpc_auth_metadata_context context,
grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
grpc_error** error) {
gpr_timespec refresh_threshold = gpr_time_from_seconds(
GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
/* See if we can return a cached jwt. */
grpc_mdelem jwt_md = GRPC_MDNULL;
{
gpr_mu_lock(&cache_mu_);
if (cached_.service_url != nullptr &&
strcmp(cached_.service_url, context.service_url) == 0 &&
!GRPC_MDISNULL(cached_.jwt_md) &&
(gpr_time_cmp(
gpr_time_sub(cached_.jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)),
refresh_threshold) > 0)) {
jwt_md = GRPC_MDELEM_REF(cached_.jwt_md);
}
gpr_mu_unlock(&cache_mu_);
}
if (GRPC_MDISNULL(jwt_md)) {
char* jwt = nullptr;
/* Generate a new jwt. */
gpr_mu_lock(&cache_mu_);
reset_cache();
jwt = grpc_jwt_encode_and_sign(&key_, context.service_url, jwt_lifetime_,
nullptr);
if (jwt != nullptr) {
char* md_value;
gpr_asprintf(&md_value, "Bearer %s", jwt);
gpr_free(jwt);
cached_.jwt_expiration =
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), jwt_lifetime_);
cached_.service_url = gpr_strdup(context.service_url);
cached_.jwt_md = grpc_mdelem_from_slices(
grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
grpc_slice_from_copied_string(md_value));
gpr_free(md_value);
jwt_md = GRPC_MDELEM_REF(cached_.jwt_md);
}
gpr_mu_unlock(&cache_mu_);
}
if (!GRPC_MDISNULL(jwt_md)) {
grpc_credentials_mdelem_array_add(md_array, jwt_md);
GRPC_MDELEM_UNREF(jwt_md);
} else {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT.");
}
return true;
}
void grpc_service_account_jwt_access_credentials::cancel_get_request_metadata(
grpc_credentials_mdelem_array* md_array, grpc_error* error) {
GRPC_ERROR_UNREF(error);
}
grpc_service_account_jwt_access_credentials::
grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,
gpr_timespec token_lifetime)
: grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_JWT), key_(key) {
gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime();
if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) {
gpr_log(GPR_INFO,
"Cropping token lifetime to maximum allowed value (%d secs).",
static_cast<int>(max_token_lifetime.tv_sec));
token_lifetime = grpc_max_auth_token_lifetime();
}
jwt_lifetime_ = token_lifetime;
gpr_mu_init(&cache_mu_);
reset_cache();
}
grpc_core::RefCountedPtr<grpc_call_credentials>
grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
grpc_auth_json_key key, gpr_timespec token_lifetime) {
if (!grpc_auth_json_key_is_valid(&key)) {
gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
return nullptr;
}
return grpc_core::MakeRefCounted<grpc_service_account_jwt_access_credentials>(
key, token_lifetime);
}
static char* redact_private_key(const char* json_key) {
char* json_copy = gpr_strdup(json_key);
grpc_json* json = grpc_json_parse_string(json_copy);
if (!json) {
gpr_free(json_copy);
return gpr_strdup("<Json failed to parse.>");
}
const char* redacted = "<redacted>";
grpc_json* current = json->child;
while (current) {
if (current->type == GRPC_JSON_STRING &&
strcmp(current->key, "private_key") == 0) {
current->value = const_cast<char*>(redacted);
break;
}
current = current->next;
}
char* clean_json = grpc_json_dump_to_string(json, 2);
gpr_free(json_copy);
grpc_json_destroy(json);
return clean_json;
}
grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
const char* json_key, gpr_timespec token_lifetime, void* reserved) {
if (grpc_api_trace.enabled()) {
char* clean_json = redact_private_key(json_key);
gpr_log(GPR_INFO,
"grpc_service_account_jwt_access_credentials_create("
"json_key=%s, "
"token_lifetime="
"gpr_timespec { tv_sec: %" PRId64
", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec,
static_cast<int>(token_lifetime.clock_type), reserved);
gpr_free(clean_json);
}
GPR_ASSERT(reserved == nullptr);
grpc_core::ExecCtx exec_ctx;
return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
grpc_auth_json_key_create_from_string(json_key), token_lifetime)
.release();
}