Skip to content

feat: add x-goog-user-project header from quota_project_id field#21324

Merged
jiangtaoli2016 merged 1 commit intogrpc:masterfrom
tmatsuo:x-goog-user-project
Feb 20, 2020
Merged

feat: add x-goog-user-project header from quota_project_id field#21324
jiangtaoli2016 merged 1 commit intogrpc:masterfrom
tmatsuo:x-goog-user-project

Conversation

@tmatsuo
Copy link
Copy Markdown

@tmatsuo tmatsuo commented Nov 27, 2019

fixes #21225

@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla bot commented Nov 27, 2019

CLA Check
The committers are authorized under a signed CLA.

@jiangtaoli2016
Copy link
Copy Markdown

Thanks @tmatsuo for the contribution. Ping me when this is ready for review.

@broady
Copy link
Copy Markdown

broady commented Dec 2, 2019

Sorry, we changed the field to quota_project_id for more consistency.

LGTM though

@tmatsuo tmatsuo marked this pull request as ready for review December 3, 2019 22:41
@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Dec 3, 2019

@jiangtaoli2016

Added some test cases. I think it's ready for review. Can you take a look?

Also, I can not add the labels. Can you add some labels for mergeable check?

Thanks!

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Dec 4, 2019

ASAN build caught the memory leak. I'll fix that with the next commit

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Dec 4, 2019

@jiangtaoli2016 I believe the memory leak has been fixed.

@jiangtaoli2016 jiangtaoli2016 added area/security kokoro:force-run release notes: yes Indicates if PR needs to be in release notes labels Dec 5, 2019
@tmatsuo tmatsuo changed the title feat: add x-goog-user-project header from quota_project field feat: add x-goog-user-project header from quota_project_id field Dec 5, 2019
Copy link
Copy Markdown

@jiangtaoli2016 jiangtaoli2016 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @tmatsuo for implementation. Looks good over all. A couple of minor comments.

Copy link
Copy Markdown
Author

@tmatsuo tmatsuo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I think I addressed the comments. PTAL

@jiangtaoli2016
Copy link
Copy Markdown

Please wait for @apolcyn review.
Also please squash before submit.

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Dec 6, 2019

@jiangtaoli2016 Thanks.

I have a question w.r.t. squashing. Do you use "Squash and merge" feature on this repo, or should I locally squash commits and force push the new change?

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Dec 11, 2019

@apolcyn friendly ping

"{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
" \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
" \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
" \"quota_project_id\": \"my-quota-project-id\","
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: quota_project _id could also be a number, right?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is always a string.

"project ID" is always a string
"project number" is always a number

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I read the spec, it was fluid. However, is it really a problem if we treat numeric ID as a string?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go/adc-spec is a (very) draft spec for the JSON file, and we've specified it as a string.

If in the future we need to support project numbers (and not treat the number as a string), it'll be a separate field, "quota_project_number".

grpc_call_credentials* creds = grpc_google_refresh_token_credentials_create(
test_refresh_token_with_quota_project_id_str, nullptr);

/* First request: http put should be called. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this comment is a little confusing, is it accurate? I don't think PUT requests are ever performed, in any case, if I'm correct?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, changed to post.

result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;

// quota_project_id is optional, so we don't check the result of the copy.
grpc_copy_json_string_property(json, "quota_project_id",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to below comment in test.

I've seen some docs that say that this can be a string or a numberic project ID. Does the format used for the refresh token credentials only allow the human readable project name format? If number format is also a possibility, then parsing may need adjusting.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@broady Can you confirm?

However, do you think it is problematic if we treat a numeric ID as a string?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answered below. It's always a string. Project number will not be in this field.

void grpc_google_refresh_token_credentials::add_additional_metadata(
grpc_credentials_mdelem_array* md_array) {
if (refresh_token_.quota_project_id != nullptr) {
grpc_mdelem quota_project_md = GRPC_MDNULL;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: unnecessary line, it can be set to grpc_mdelem_from_slices

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done

Copy link
Copy Markdown
Author

@tmatsuo tmatsuo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apolcyn Thanks for the review! I addressed some comments. PTAL.

void grpc_google_refresh_token_credentials::add_additional_metadata(
grpc_credentials_mdelem_array* md_array) {
if (refresh_token_.quota_project_id != nullptr) {
grpc_mdelem quota_project_md = GRPC_MDNULL;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done

grpc_call_credentials* creds = grpc_google_refresh_token_credentials_create(
test_refresh_token_with_quota_project_id_str, nullptr);

/* First request: http put should be called. */
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, changed to post.

"{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
" \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
" \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
" \"quota_project_id\": \"my-quota-project-id\","
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I read the spec, it was fluid. However, is it really a problem if we treat numeric ID as a string?

Copy link
Copy Markdown
Contributor

@apolcyn apolcyn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor comments.

A couple question on the feature:

  • I believe the way to use "x-goog-user-project" so far has been to just set it manually; is there possibility that when the quota_project field is added to json auth files for the first time, that it can conflict with such usage?
  • Is the quota_project_id value that is added to auth json files just specified by user?

grpc_polling_entity* pollent, grpc_auth_metadata_context /*context*/,
grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
grpc_error** /*error*/) {
add_additional_metadata(md_array);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming nit: can we prefix this with maybe, i.e. change to maybe_add_additional_metadata

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// auth_refresh_token parsing.
typedef struct {
struct grpc_auth_refresh_token {
grpc_auth_refresh_token() : quota_project_id(nullptr) {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can remove this line and set quota_project_id = nullptr below

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown
Author

@tmatsuo tmatsuo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the way to use "x-goog-user-project" so far has been to just set it manually; is there possibility that when the quota_project field is added to json auth files for the first time, that it can conflict with such usage?

I don't think it's a problem.

Is the quota_project_id value that is added to auth json files just specified by user?

Mainly this is auto selected by gcloud sdk depending on the user configuration (config->project).

Adding @broady to confirm.

grpc_polling_entity* pollent, grpc_auth_metadata_context /*context*/,
grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
grpc_error** /*error*/) {
add_additional_metadata(md_array);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// auth_refresh_token parsing.
typedef struct {
struct grpc_auth_refresh_token {
grpc_auth_refresh_token() : quota_project_id(nullptr) {}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Dec 11, 2019

@apolcyn

After doing some experiment, it turns out that, if you specify x-goog-user-project manually like:

context.AddMetadata("x-goog-user-project", "some-other-project");

and if the auth json file contains quota_project_id, it is actually a problem.

In this case, it seems like those values are combined with a comma and I'm getting an error message as follows

Project 'some-other-project,my-quota-project' not found or deleted.

The initial thing came to mind was to check contents of the md_array and only add x-goog-user-project when the key doesn't exist in grpc_google_refresh_token_credentials::maybe_add_additional_metadata function. However, it doesn't work because md_array is always empty there.

So I'm not sure how to handle this case. Is there a nice way to disallow duplicated keys in metadata?

@apolcyn
Copy link
Copy Markdown
Contributor

apolcyn commented Dec 12, 2019

So I'm not sure how to handle this case. Is there a nice way to disallow duplicated keys in metadata?

Unfortunately, I'm not sure that there is, at least from within the the oauth2 credentials implementation. It may be possible to do some sort of header interception for this e.g. within the client_auth_filter, but that would probably need some larger changes.

But I'm also wondering if handling that is really necessary, this is why I wonder "how is the quota_project_id" being set within the auth json files.

If "quota_project_id" being filled into auth json files is an opt-in thing, then maybe we don't need to handle this, since onus is on the user to not do something that will conflict with their code.

OTOH if it's going to be filled in transparently, then it could perhaps cause problems for existing code.

What do you think?

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Dec 12, 2019

@apolcyn Thanks. I don't think the change will be an opt-in.

However, currently x-goog-user-project metadata header is not documented anywhere. Also the auth json file with the type authorized_user is only for development purpose. So I think the user impact will be negligible.

I'm on the fence. I'll take a look at the client_auth_filter code and then think about this again.

@broady
Copy link
Copy Markdown

broady commented Dec 12, 2019 via email

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Dec 12, 2019

@apolcyn
@broady

Thanks,

I took a look at client_auth_filter, I don't think it is a trivial change to handle duplicate metadata key. Also it's probably better to separate PRs, even if we decide to handle this case.

So, now I think we should proceed with the current code.

WDYT?

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Jan 10, 2020

@apolcyn @jiangtaoli2016
PTAL
The last commit add the metadata only when the same key doesn't exist.

@jiangtaoli2016
Copy link
Copy Markdown

Adding one-off check on generic client_auth_filter seems hacky.
@apolcyn do you know if we can put the check on oauth2_credentials.cc?

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Jan 14, 2020

@jiangtaoli2016
I understand. How about to have a new function like grpc_metadata_batch_add_tail_when_key_not_exist or something in metadata_batch.cc? It will make the code little bit cleaner and more generic I think.

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Jan 14, 2020

@jiangtaoli2016 The last commit added grpc_metadata_batch_add_tail_when_key_not_exist function.

@apolcyn
Copy link
Copy Markdown
Contributor

apolcyn commented Jan 15, 2020

@jiangtaoli2016

Adding one-off check on generic client_auth_filter seems hacky.
@apolcyn do you know if we can put the check on oauth2_credentials.cc?

I don't think it's possible to put the check in oauth2_credentials.cc, at least not without some sort of change to the get_request_metadata API, since oauth2_credentials doesn't currently have a way to access the initial_metadata of the grpc_transport_stream_op_batch that the client auth filter is working with.

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Jan 17, 2020

@jiangtaoli2016

PTAL with the last commit. The code is now little bit more generic and cleaner.

Copy link
Copy Markdown

@jiangtaoli2016 jiangtaoli2016 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for delay. Thanks @tmatsuo for revision.

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Jan 28, 2020

@apolcyn I have just squashed the commits. Do you mind taking a look and merge it if it looks ok?

@tmatsuo tmatsuo force-pushed the x-goog-user-project branch from 0c66117 to 9bed17b Compare January 28, 2020 21:36
Copy link
Copy Markdown
Contributor

@apolcyn apolcyn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for delay. LGTM but I'll actually ask @yashykt to take a second look at the metadata_batch.{h.cc} change

@apolcyn apolcyn requested a review from yashykt January 29, 2020 19:48
@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Feb 5, 2020

@yashykt friendly ping

@tmatsuo
Copy link
Copy Markdown
Author

tmatsuo commented Feb 7, 2020

@jiangtaoli2016 It's approved. Can you go ahead and merge this?

@jiangtaoli2016 jiangtaoli2016 self-assigned this Feb 20, 2020
@jiangtaoli2016 jiangtaoli2016 added lang/core release notes: yes Indicates if PR needs to be in release notes and removed release notes: yes Indicates if PR needs to be in release notes labels Feb 20, 2020
@jiangtaoli2016
Copy link
Copy Markdown

test failures unrelated.

@jiangtaoli2016 jiangtaoli2016 merged commit ddd0107 into grpc:master Feb 20, 2020
codeblooded pushed a commit to codeblooded/grpc that referenced this pull request Feb 21, 2020
feat: add x-goog-user-project header from quota_project_id field
@lock lock bot locked as resolved and limited conversation to collaborators May 20, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area/security lang/core release notes: yes Indicates if PR needs to be in release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C++ auth library: Add x-goog-user-project metadata header for outgoing requests

6 participants