Skip to content

Enable ESP to invoke Firebase Security rules. - #54

Merged
sarvaniv merged 22 commits into
istio:firebasefrom
sarvaniv:firebase
Feb 2, 2017
Merged

Enable ESP to invoke Firebase Security rules.#54
sarvaniv merged 22 commits into
istio:firebasefrom
sarvaniv:firebase

Conversation

@sarvaniv

Copy link
Copy Markdown
Contributor

No description provided.

@qiwzhang

Copy link
Copy Markdown
Contributor

You need to use clang-format to format following files

contrib/endpoints/src/api_manager/check_security_rules.cc contrib/endpoints/src/api_manager/check_security_rules.h contrib/endpoints/src/api_manager/context/request_context.h contrib/endpoints/src/api_manager/context/service_context.cc

std::string auth_authorized_party_;

// Auth Claims: This is the decoded payload of the JWT token
char *auth_claims_;

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.

We can use std::string to store it. so you don't need to include grpc headers in order to call gpr_free

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

// See http://openid.net/specs/openid-connect-core-1_0.html#IDToken
std::string authorized_party;
// String of claims
char *claims;

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.

do we need this one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes. We do. The UserInfo is where the decoded JWT claims are stored so it can be used in the later part of the work flow. This actually is the same flow as it is used for JWT audience and issuer fields.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed it to std::string

} else {
env_->LogInfo("Before Parse JWT");
ParseJwt();
env_->LogInfo("After Parse JWT");

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.

remove logging?

Copy link
Copy Markdown
Contributor 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
Contributor Author

Choose a reason for hiding this comment

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

Looks like the merge put them back in. I will delete them before the next commit.

void HttpFetch(const std::string &url, const std::string &request_body,
std::function<void(Status, std::string &&)> continuation);
// Fetch the Release attributes.
auto pChecker = GetPtr();

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.

Google c++ style guide, not to use camel case in variable name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm. This code is again similar to check_auth.cc below. Let me know if you want me to fix it in both places.

void AuthChecker::DiscoverJwksUri(const std::string &url) {
auto pChecker = GetPtr();
HttpFetch(url, [pChecker](S

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

// TODO: Check service config to see if "useSecurityRules" is specified.
// If so, call Firebase Rules service TestRuleset API.
std::pair<Status, std::string> AuthzChecker::ParseReleaseResponse(
std::string *json_str) {

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.

pass in const std::string& if you are not modifying json_str

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

void AuthzChecker::Check() {
// TODO: Check service config to see if "useSecurityRules" is specified.
// If so, call Firebase Rules service TestRuleset API.
std::pair<Status, std::string> AuthzChecker::ParseReleaseResponse(

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.

can we pass out ruleset_id in the argument. e.g.
Status Parse(..., string* ruleset_id)

Copy link
Copy Markdown
Contributor 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
Contributor Author

Choose a reason for hiding this comment

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

Done

status = Status(Code::PERMISSION_DENIED,
std::string("Unauthorized ")
+ context->request()->GetRequestHTTPMethod()
+ " access to resource " + context->request()->GetRequestPath(),

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.

Does response have detail info we can pass back to help users?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No. Unfortunatley, the Test API that we use only provided with a response that looks like this:

{
"testResults": [
{
"state": "SUCCESS"
}
]
}

or

{
"testResults": [
{
"state": "FAILURE"
}
]
}

The response itself is a HTTP 200 OK message.

std::string AuthzChecker::BuildTestRequestBody(
std::shared_ptr<context::RequestContext> context) {

std::shared_ptr<std::ostringstream> ss(new std::ostringstream);

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.

not need to use new. just allocate it from stack.

Copy link
Copy Markdown
Contributor 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
Contributor Author

Choose a reason for hiding this comment

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

Done

std::shared_ptr<AuthzChecker> authzChecker =
std::make_shared<AuthzChecker>(context, continuation);
authzChecker->Check();
std::shared_ptr<AuthzChecker> checker(new AuthzChecker(

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.

why don't you use make_shared?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

std::string BuildTestRequestBody(
std::shared_ptr<context::RequestContext> context);

void AddToBody(const std::string &key,

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.

can some of these function static.

if so, they can be in the cc file, inside anonymous name space

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I moved the header file into the .cc file now.

#include "grpc/support/string_util.h"
#include "grpc/support/sync.h"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we need all these headers? It looks like we are only using gpr_free() in the file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed them. After changing to std::string based on Wayne's comments, I did not need them.

if (cache_hit) {
CheckAudience(true);
} else {
env_->LogInfo("Before Parse JWT");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remember remove these logs before submit.

Copy link
Copy Markdown
Contributor 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
Contributor Author

Choose a reason for hiding this comment

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

Done

} else {
env_->LogInfo("Before Parse JWT");
ParseJwt();
env_->LogInfo("After Parse JWT");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remove these logs before submit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

// See http://openid.net/specs/openid-connect-core-1_0.html#IDToken
std::string authorized_party;
// String of claims
char *claims;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can we make it std::string?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.


// Get the auth token for Firebase service
const std::string &GetAuthToken() {
return sa_token_->GetAuthToken(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shall we check if sa_token_ is NULL?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm. This is similar to The Aggregator code in cloud_trace.cc where the ServiceAccountToken is being passed in. There are no null checks there. Let me know if you think this is wrong and we can fix this at book places. However, since we don't throw exceptions, the null check should be done before passing in the value. Otherwise, we will have null checks all over the code.

std::shared_ptr<std::ostringstream> ss);

void AddToBody(const std::string &key, const std::string &value,
bool end, std::shared_ptr<std::ostringstream> ss);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why do we need shared_ptr here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

REmoved

std::shared_ptr<context::RequestContext> context);

void AddToBody(const std::string &key,
std::shared_ptr<std::ostringstream> ss);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why do we need shared_ptr here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed


const std::string Stringify(const char *s) {
return std::string("\"") + s + "\"";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You are not handing the case that s is NULL pointer.

Converting a "char *" to string is simply:

std::string target = s == nullptr ? "" : s;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

.set_url(url)
.set_auth_token(GetAuthToken())
.set_header("Content-Type", "application/json")
.set_auth_token(token);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

set_auth_token(GetAuthToken()). Then you don't need "token" variable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

class AuthzChecker : public std::enable_shared_from_this<AuthzChecker> {
public:
AuthzChecker(std::shared_ptr<context::RequestContext> context,
std::function<void(Status status)> continuation);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If AuthzChecker class is used only in check_seucurity_rules.cc/h, we prefer to put the class definition in .cc file. This way, any place that "include" check_security_rules.h will only include CheckSecurityRules() function definition (not AuthzChecker class).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

// Get Firebase specific operation Id based on the http Method.
std::string GetOperation(std::string httpMethod);

const std::string Stringify(const char *s) {

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.

no need to be a class function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is removed with the new protobuf change.s

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed this after proto change.

}

// Fetch the Release attributes.
auto pchecker = GetPtr();

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.

just "checker"

Copy link
Copy Markdown
Contributor 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
Contributor Author

Choose a reason for hiding this comment

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

done.


// Fetch the Release attributes.
auto pchecker = GetPtr();
HttpFetch(GetReleaseUrl(context), std::string("GET"), std::string(""),

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.

no need for std::string("GET"), just "GET", compiler will convert it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

std::function<void(Status status)> continuation) {
auto pchecker = GetPtr();

HttpFetch(std::string(kFirebaseServerStaging) + "v1/" + ruleset_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.

use a separate function for URL


Status status = Status::OK;
const char *id = GetStringValue(json, "rulesetName");
(*ruleset_id) = (id == nullptr) ? "" : GetStringValue(json, "rulesetName");

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.

last portion is id?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure what you mean ...

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.

*ruleset_id = (id) : id ? "";

return status;
}

std::string AuthzChecker::GetOperation(std::string httpMethod) {

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.

need to be a class member function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Made it static function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Made it static.

std::string AuthzChecker::BuildTestRequestBody(
std::shared_ptr<context::RequestContext> context) {

std::ostringstream ss;

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.

maybe use a proto, build a proto message first, then convert it to json.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

How I wish I could do it! The protos are not open sourced.

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.

You can copy the proto file to our ESP code base. Still prefer to use proto to convert to JSON.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There are multiple proto files and this will come from the firebase codebase. I am not sure if we can just pick up the code and put it in our codebase. I will need to talk to Limin / Wencheng and may be even Firebase guys before doing this.

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.

If it is too much trouble to copy their proto files, you just create your own simple proto message which only fields you used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I see what you mean. I think I an do that. It is actually a better idea than dealing with strings.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

}

void AuthzChecker::AddToBody(const std::string &key,
std::ostringstream &ss) {

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.

no need to be a class member function

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The reason I left it inside the class is because the usage of the function is only applicable to objects of this class.

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.

it should be static if the function is not using any class members.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah. Sure. I will make them static.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Function is deleted after adding the new protobuf to capture this.

}
}

const std::string AuthzChecker::GetReleaseName(

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.

no need to be a class member function

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same reason as above. There is no meaning to this method outside the context of the class.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed this to static

// Pointer to access ESP running environment.
ApiManagerEnvInterface *env_;
// Get Firebase specific operation Id based on the http Method.
std::string GetOperation(std::string httpMethod);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

const std::string &httpMethod

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

#include "contrib/endpoints/include/api_manager/utils/status.h"

#include <string>
using ::google::api_manager::utils::Status;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No change is needed for this file. The additional "include" and "using" here should be removed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

last_report_time_ = tp;
}

void set_auth_claims(std::string &claims) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

const std::string &

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

return auth_claims_;
}

const std::string GetAuthToken();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why do you need GetAuthToken() here?

namespace api_manager {

namespace {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The whole class should be an anonymous namespace. You should not remove it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

"https://staging-firebaserules.sandbox.googleapis.com/";

// An AuthzChecker object is created for every incoming request. It does
// authorizaiton by calling Firebase Rules service.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment applies to the class. Why do you remove it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added it back. Initially I had code that was creating the object in the service context but this did not work due to circular dependences. I removed it as a part of that change and forgot to add it back.

void HttpFetch(const std::string &url, const std::string &request_body,

// Helper method that invokes the test firebase service api.
void FirebaseCheck(std::string ruleset_id,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

const std::string &ruleset_id

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

last_report_time_ = tp;
}

void set_auth_claims(std::string &claims) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

const std::string &claims

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

auth_claims_ = claims;
}

std::string &auth_claims() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this function can simply return std::string.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why? We just use this string to create a proto object and this string can be potentially big.


/*for (grpc_json *cur = json->child; cur != nullptr; cur = cur->next) {
user_info->claims.emplace(std::string(cur->key), std::string(cur->value));
} */

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.

what is this commented out code for? If no needed, remove it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed.

message ApiCheckSecurityRulesConfig {
// Allows to disable the API authorization
bool force_disable = 1;
}

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.

you may want to have firebase_server here so "t" test can provide a fake server.

BTW, How do we enable this feature, is it enabled by service_config? Here we force disable it? If so, we are fine

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This can be enabled via service config. But the service config changes are not done yet. Tao is working on those changes.

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.

If that is the case, it will be easier for you to integrate with ESP if you default to be disabled, only enable it from server_config.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't want to disable this by default via server config and there is a reason for this. By not disabling the feature by default I am able to apply some existing auth based t tests. For example, we have checks to ensure that if auth is disabled, then we don't do any firebase rules checks too. For all those tests, since the code is not disabled by default, we are able to exercise the existing t tests without any more tests that do the same thing.

->api_authentication_config()
.force_disable()) {
.force_disable()),
is_check_security_rules_disabled_(config_->server_config() &&

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.

is it enabled by default? do we need to check service_config ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Currently enabled by default. Once the service config changes are done, we will add those checks too.

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.

it is better to be disabled by default for now. and only enable it when server_config has api_check_securty_rules_config and its firebase_server is specified.

we can change it when service config is ready.


// An AuthzChecker object is created for every incoming request. It does
// authorizaiton by calling Firebase Rules service.
class AuthzChecker : public std::enable_shared_from_this<AuthzChecker> {

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.

Since the file name is called check_security_rules, should we call the class as SecurityRuleChecker

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was named by Limin and so was the file name provided by her. Let me see what she thinks too :-)

void HttpFetch(const std::string &url, const std::string &request_body,
// Helper method that invokes the test firebase service api.
void FirebaseCheck(std::string &ruleset_id,
std::shared_ptr<context::RequestContext> context,

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.

how about "CallTest"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

void AuthzChecker::FirebaseCheck(
std::string &ruleset_id, std::shared_ptr<context::RequestContext> context,
std::function<void(Status status)> continuation) {
auto checker = GetPtr();

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.

move this line to 163

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

std::shared_ptr<context::RequestContext> context,
std::string &result_string) {
proto::TestRulesetRequest request;
proto::TestRulesetRequest::TestCase *test_case = request.add_test_cases();

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.

you can use "auto"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yup. Thank you.


Status AuthzChecker::BuildTestRequestBody(
std::shared_ptr<context::RequestContext> context,
std::string &result_string) {

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.

Per style guide, you should use std::string*

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.


void AuthzChecker::SetProtoValue(::google::protobuf::Value &head,
std::string key,
::google::protobuf::Value &value) {

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.

per style guide, the prototype should be
(const string& key, const Value& value, Value* head)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.


// Pointer to access ESP running environment.
ApiManagerEnvInterface *env_;
void SetProtoValue(::google::protobuf::Value &head, std::string key,

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.

this can be static.

I think all these 4 static functions can be out of the class.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

if (!context->service_context()->RequireRulesCheck() ||
context->method() == nullptr || !context->method()->auth()) {
env_->LogDebug(
std::string("Autherization and JWT validation was not performed") +

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: Authorization, no need of std::string, just use literals without +

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

std::string *ruleset_id);

// Parses the response for the TEST API call
Status ParseTestResponse(std::shared_ptr<context::RequestContext> context,

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.

output parameter should be pointers, per our C++ style guide

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes. Wayne gave me this comment and I have already fixed this. in a later commit.

const std::string &json_str);

// Builds the request body for the TESP API call.
Status BuildTestRequestBody(std::shared_ptr<context::RequestContext> context,

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.

ditto

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

same. already fixed in later commit.

if (result == nullptr) {
env_->LogInfo("Result state is empty");
status = invalid;
} else if (std::string(result) != "SUCCESS") {

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.

"SUCCESS" as constant, and make the constant as std::string.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have declared a const char[] kTestSuccess with the rest of the cont strings. Let me know if this is not what you had in mind.


void set_auth_claims(const std::string &claims) { auth_claims_ = claims; }

std::string &auth_claims() { return auth_claims_; }

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.

const

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

#ifndef API_MANAGER_AUTH_H_
#define API_MANAGER_AUTH_H_

#include <map>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why do you include ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed.

// Helper function to send a http GET request.
void HttpFetch(const std::string &url, const std::string &request_body,
// Helper method that invokes the test firebase service api.
void CallTest(std::string &ruleset_id,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

const std::string &ruleset_id;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

#ifndef API_MANAGER_CHECK_SECURITY_RULES_H_
#define API_MANAGER_CHECK_SECURITY_RULES_H_

#include <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.

remove "include".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

// Server config for API Authorization via Firebase Rules
message ApiCheckSecurityRulesConfig {
// Firebase server to use.
string firebase_server = 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When do we use this configuration?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We will use this configuration with t tests. I will update this in another change once I understand t tests better. For now this is only a place holder.

// Optional field.
const grpc_json *grpc_json = grpc_jwt_claims_json(claims_);

char *json_str =

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.

json_str is leaked

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. Missed this while addressing review comments. I was freeing this in the first commit :-)

const char kInvalidResponse[] = "Invalid JSON response from Firebase Service";
const char kTestSuccess[] = "SUCCESS";

void SetProtoValue(std::string key, ::google::protobuf::Value &value,

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.

const std::string&
const Value&

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

}

const std::string GetReleaseName(
std::shared_ptr<context::RequestContext> request_context) {

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.

Not to use shared_ptr if you are not keep a ref_count.
use
const context::RequestContext&

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The CheckSecurityRules method is the entry point and it receives a share_ptr. Do you mean that I should extract the pointer from the shared pointer? I thought it is considered bad to extract a pointer out of a smart pointer. Lets discuss more.

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.

Pass a shared_ptr is bad for performance. it needs to lock in order to increase ref_count.

If your function don't need to keep a ref_count, pass in the object.

}

const std::string GetReleaseUrl(
std::shared_ptr<context::RequestContext> request_context) {

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.

not to use shared_prt

if (!context->service_context()->RequireRulesCheck() ||
context->method() == nullptr || !context->method()->auth()) {
env_->LogDebug(
"Autherization and JWT validation was not performed"

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.

is logging message correct?

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.

should it be firebase ruleset validation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed

Status status = Status::OK;
const char *id = GetStringValue(json, "rulesetName");
(*ruleset_id) = (id == nullptr) ? "" : 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.

why do we need () in (*ruleset_id)?

SetProtoValue("token", claims, &token);
SetProtoValue("auth", token, &auth);

Map<std::string, google::protobuf::Value> *variables =

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.

use auto?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

const std::string &request_body,
std::function<void(Status, std::string &&)> continuation) {
env_->LogInfo(std::string("Issue HTTP Request to url :") + url +
" method : " + method + " body: " + request_body);

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.

LogDebug

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

->api_authentication_config()
.force_disable()) {
.force_disable()),
is_check_security_rules_disabled_(config_->server_config() &&

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.

it is better to be disabled by default for now. and only enable it when server_config has api_check_securty_rules_config and its firebase_server is specified.

we can change it when service config is ready.

std::shared_ptr<context::RequestContext> request_context) {
return request_context->service_context()->service_name() + ":" +
request_context->service_context()->service().apis(0).version();
const std::string GetReleaseName(context::RequestContext &context) {

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.

do we need const here? same as line 65

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.

and line 70

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I already tried that and I get the following compilation error. I think this is because the service_context() method is not marked as const. Let me know if you are OK with marking that method as const - I think it can be made into a const but I don't know why it was not marked const.

contrib/endpoints/src/api_manager/check_security_rules.cc: In function 'const string google::api_manager::{anonymous}::GetReleaseName(const google::api_manager::context::RequestContext&)':
contrib/endpoints/src/api_manager/check_security_rules.cc:61:34: error: passing 'const google::api_manager::context::RequestContext' as 'this' argument of 'google::api_manager::context::ServiceContext* google::api_manager::context::RequestContext::service_context()' discards qualifiers [-fpermissive]
return context.service_context()->service_name() + ":" +
^
contrib/endpoints/src/api_manager/check_security_rules.cc:62:34: error: passing 'const google::api_manager::context::RequestContext' as 'this' argument of 'google::api_manager::context::ServiceContext* google::api_manager::context::RequestContext::service_context()' discards qualifiers [-fpermissive]
context.service_context()->service().apis(0).version();

auto checker = GetPtr();
HttpFetch(GetReleaseUrl(context), "GET", "",
HttpFetch(GetReleaseUrl(*context.get()), "GET", "",
[context, final_continuation, checker](Status status,

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.

could we just do "*context" to convert shared_ptr to const T&?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

return RequireAuth() && config_->server_config() &&
!config_->server_config()
->api_check_security_rules_config()
.firebase_server()

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.

you may need to call has_ first before you access its members.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added it. For some reason this is not being done in other parts of the code. I am adding this check in one place I know.

char *json_str =
grpc_json_dump_to_string(const_cast<::grpc_json *>(grpc_json), 0);
user_info->claims = json_str == nullptr ? "" : json_str;
gpr_free(json_str);

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.

you probably want to check if json_str is nullptr

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oops.

const char kFirebaseServerStaging[] =
"https://staging-firebaserules.sandbox.googleapis.com/";

const char kFirebaseService[] =

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.

where are these two constants used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I will delete them once i have done more testing.


std::string GetOperation(std::string &httpMethod) {
if (httpMethod == "POST") {
return std::string("create");

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.

make these constant

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. Some parts of the code were using them as I have done. I did not know this was a strict rule.

request->set_method(method).set_url(url).set_auth_token(GetAuthToken());

if (method != "GET") {
request->set_header("Content-Type", "application/json")

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.

make these constant too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have removed as many string constants from code as I could. Let me know if you think there are more that need to be changed.

@qiwzhang qiwzhang left a comment

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.

Just add a simple config_test for firebase_server for now.
In a separate PR, add check_security_rules_test.cc, similar to auth_check_test.cc


const std::string GetFirebaseServer(const context::RequestContext &context) {
return context.service_context()
->config()

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.

make a GetFireBaseServer() function in Config class. Later when service_config is ready, get it from there. Only need to change that function.

const char kContentType[] = "Content-Type";
const char kApplication[] = "application/json";

const std::string GetFirebaseServer(const context::RequestContext &context) {

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.

I still don't understand the purpose of "const" in

const std::string foo()

what is this const for?


const std::string GetReleaseName(const context::RequestContext &context) {
return context.service_context()->service_name() + ":" +
context.service_context()->service().apis(0).version();

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.

when enabling firebase, need to make sure apis.size() == 1.

grpc_json_dump_to_string(const_cast<::grpc_json *>(grpc_json), 0);
user_info->claims = json_str == nullptr ? "" : json_str;
if (json_str != nullptr) {
gpr_free(json_str);

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.

move the line above in here? member std::string is default to "", so you just need to assign once.

bool IsRulesCheckEnabled() const {
return RequireAuth() && config_->server_config() &&
return RequireAuth() && service().apis_size() > 0 &&
config_->server_config() &&

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.

can we call config->GEtFirebaseServer()?


auto server = config->GetFirebaseServer();
ASSERT_EQ(server, "https://myfirebaseserver.com/");
}

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.

could you add a test case with a non-empty server_config, but no api_check_security_rules_config.

@sarvaniv
sarvaniv merged commit df8c69c into istio:firebase Feb 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants