blob: 8705df785f00c25ca158ea69bd62daba927f6140 [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
//
// 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 <memory>
#include "cobalt/h5vcc/h5vcc_account_manager.h"
#include "base/command_line.h"
#include "base/memory/weak_ptr.h"
#include "cobalt/browser/switches.h"
#include "starboard/user.h"
namespace cobalt {
namespace h5vcc {
H5vccAccountManager::H5vccAccountManager()
: user_authorizer_(account::UserAuthorizer::Create()),
owning_message_loop_(base::MessageLoop::current()),
thread_("AccountManager") {
thread_.Start();
}
void H5vccAccountManager::GetAuthToken(
const AccessTokenCallbackHolder& callback) {
DLOG(INFO) << "Get authorization token.";
PostOperation(kGetToken, callback);
}
void H5vccAccountManager::RequestPairing(
const AccessTokenCallbackHolder& callback) {
DLOG(INFO) << "Request application linking.";
PostOperation(kPairing, callback);
}
void H5vccAccountManager::RequestUnpairing(
const AccessTokenCallbackHolder& callback) {
DLOG(INFO) << "Request application unlinking.";
PostOperation(kUnpairing, callback);
}
void H5vccAccountManager::PostOperation(
OperationType operation_type, const AccessTokenCallbackHolder& callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
AccessTokenCallbackReference* token_callback =
new AccessTokenCallbackHolder::Reference(this, callback);
pending_callbacks_.push_back(
std::unique_ptr<AccessTokenCallbackReference>(token_callback));
thread_.message_loop()->task_runner()->PostTask(
FROM_HERE, base::Bind(&H5vccAccountManager::RequestOperationInternal,
user_authorizer_.get(), operation_type,
base::Bind(&H5vccAccountManager::PostResult,
owning_message_loop_,
base::AsWeakPtr(this), token_callback)));
}
H5vccAccountManager::~H5vccAccountManager() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Give the UserAuthorizer a chance to abort any long running pending requests
// before the message loop gets shut down.
user_authorizer_->Shutdown();
}
// static
void H5vccAccountManager::RequestOperationInternal(
account::UserAuthorizer* user_authorizer, OperationType operation,
const base::Callback<void(const std::string&, uint64_t)>& post_result) {
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(browser::switches::kDisableSignIn)) {
post_result.Run(std::string(), 0);
return;
}
#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
SbUser current_user = SbUserGetCurrent();
DCHECK(SbUserIsValid(current_user));
std::unique_ptr<account::AccessToken> access_token(nullptr);
switch (operation) {
case kPairing:
access_token = user_authorizer->AuthorizeUser(current_user);
DLOG_IF(INFO, !access_token) << "User authorization request failed.";
break;
case kUnpairing:
if (user_authorizer->DeauthorizeUser(current_user)) {
break;
}
// The user canceled the flow, or there was some error. Fall into the next
// case to get an access token if available and return that.
DLOG(INFO) << "User deauthorization request failed. Try to get token.";
case kGetToken:
access_token = user_authorizer->RefreshAuthorization(current_user);
DLOG_IF(INFO, !access_token) << "Authorization refresh request failed.";
break;
}
std::string token_value;
uint64_t expiration_in_seconds = 0;
if (access_token) {
token_value = access_token->token_value;
if (access_token->expiry) {
base::TimeDelta expires_in =
access_token->expiry.value() - base::Time::Now();
// If this token's expiry is in the past, then it's not a valid token.
base::TimeDelta zero;
if (expires_in < zero) {
DLOG(WARNING) << "User authorization expires in the past.";
token_value.clear();
} else {
expiration_in_seconds = expires_in.InSeconds();
}
}
}
post_result.Run(token_value, expiration_in_seconds);
}
// static
void H5vccAccountManager::PostResult(
base::MessageLoop* message_loop,
base::WeakPtr<H5vccAccountManager> h5vcc_account_manager,
AccessTokenCallbackReference* token_callback, const std::string& token,
uint64_t expiration_in_seconds) {
message_loop->task_runner()->PostTask(
FROM_HERE,
base::Bind(&H5vccAccountManager::SendResult, h5vcc_account_manager,
token_callback, token, expiration_in_seconds));
}
void H5vccAccountManager::SendResult(
AccessTokenCallbackReference* token_callback, const std::string& token,
uint64_t expiration_in_seconds) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::vector<std::unique_ptr<AccessTokenCallbackReference>>::iterator found =
std::find_if(pending_callbacks_.begin(), pending_callbacks_.end(),
[token_callback](
const std::unique_ptr<AccessTokenCallbackReference>& i) {
return i.get() == token_callback;
});
if (found == pending_callbacks_.end()) {
DLOG(ERROR) << "Account manager callback not valid.";
return;
}
// In case a new account manager request is made as part of the callback,
// erase the callback in the pending vector before running it, but we can't
// delete it until after we've made the callback.
found->release();
pending_callbacks_.erase(found);
token_callback->value().Run(token, expiration_in_seconds);
delete token_callback;
}
} // namespace h5vcc
} // namespace cobalt