Skip to content

Commit

Permalink
Add AndroidManagementClient
Browse files Browse the repository at this point in the history
Add check_android_management device management request/response.

BUG=602612

Review-Url: https://codereview.chromium.org/1892093002
Cr-Commit-Position: refs/heads/master@{#390620}
  • Loading branch information
pbond authored and Commit bot committed Apr 29, 2016
1 parent 0a7f020 commit bcd3a5f
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 1 deletion.
116 changes: 116 additions & 0 deletions chrome/browser/chromeos/policy/android_management_client.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/policy/android_management_client.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/guid.h"
#include "base/logging.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "policy/proto/device_management_backend.pb.h"

namespace em = enterprise_management;

namespace policy {

AndroidManagementClient::AndroidManagementClient(
DeviceManagementService* device_management_service,
scoped_refptr<net::URLRequestContextGetter> request_context,
const std::string& account_id,
OAuth2TokenService* token_service)
: OAuth2TokenService::Consumer("android_management_client"),
device_management_service_(device_management_service),
request_context_(request_context),
account_id_(account_id),
token_service_(token_service),
weak_ptr_factory_(this) {}

AndroidManagementClient::~AndroidManagementClient() {}

void AndroidManagementClient::StartCheckAndroidManagement(
const StatusCallback& callback) {
DCHECK(device_management_service_);
DCHECK(callback_.is_null());

callback_ = callback;
RequestAccessToken();
}

void AndroidManagementClient::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) {
DCHECK_EQ(token_request_.get(), request);
token_request_.reset();

CheckAndroidManagement(access_token);
}

void AndroidManagementClient::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
DCHECK_EQ(token_request_.get(), request);
token_request_.reset();
LOG(ERROR) << "Token request failed: " << error.ToString();

callback_.Run(RESULT_ERROR);
callback_.Reset();
}

void AndroidManagementClient::RequestAccessToken() {
DCHECK(!token_request_);
// The user must be signed in already.
DCHECK(token_service_->RefreshTokenIsAvailable(account_id_));

OAuth2TokenService::ScopeSet scopes;
scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
scopes.insert(GaiaConstants::kOAuthWrapBridgeUserInfoScope);
token_request_ = token_service_->StartRequest(account_id_, scopes, this);
}

void AndroidManagementClient::CheckAndroidManagement(
const std::string& access_token) {
request_job_.reset(device_management_service_->CreateJob(
DeviceManagementRequestJob::TYPE_ANDROID_MANAGEMENT_CHECK,
request_context_.get()));
request_job_->SetOAuthToken(access_token);
request_job_->SetClientID(base::GenerateGUID());
request_job_->GetRequest()->mutable_check_android_management_request();

request_job_->Start(
base::Bind(&AndroidManagementClient::OnAndroidManagementChecked,
weak_ptr_factory_.GetWeakPtr()));
}

void AndroidManagementClient::OnAndroidManagementChecked(
DeviceManagementStatus status,
int net_error,
const em::DeviceManagementResponse& response) {
if (status == DM_STATUS_SUCCESS &&
!response.has_check_android_management_response()) {
LOG(WARNING) << "Invalid check android management response.";
status = DM_STATUS_RESPONSE_DECODING_ERROR;
}

Result result;
switch (status) {
case DM_STATUS_SUCCESS:
result = RESULT_UNMANAGED;
break;
case DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
result = RESULT_MANAGED;
break;
default:
result = RESULT_ERROR;
}

request_job_.reset();
callback_.Run(result);
callback_.Reset();
}

} // namespace policy
100 changes: 100 additions & 0 deletions chrome/browser/chromeos/policy/android_management_client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_CHROMEOS_POLICY_ANDROID_MANAGEMENT_CLIENT_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_ANDROID_MANAGEMENT_CLIENT_H_

#include <memory>
#include <string>

#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "google_apis/gaia/oauth2_token_service.h"
#include "net/url_request/url_request_context_getter.h"

namespace enterprise_management {
class DeviceManagementResponse;
}

namespace policy {

class DeviceManagementRequestJob;
class DeviceManagementService;

// Interacts with the device management service and determines whether Android
// management is enabled for the user or not. Uses the OAuth2TokenService to
// acquire access tokens for the device management.
class AndroidManagementClient : public OAuth2TokenService::Consumer {
public:
// Indicates result of the android management check.
enum Result {
RESULT_MANAGED, // Android management is enabled.
RESULT_UNMANAGED, // Android management is disabled.
RESULT_ERROR, // Received a error.
};

// A callback which receives Result status of an operation.
using StatusCallback = base::Callback<void(Result)>;

AndroidManagementClient(
DeviceManagementService* service,
scoped_refptr<net::URLRequestContextGetter> request_context,
const std::string& account_id,
OAuth2TokenService* token_service);
~AndroidManagementClient() override;

// Starts sending of check Android management request to DM server, issues
// access token if neccessary. |callback| is called on check Android
// management completion.
void StartCheckAndroidManagement(const StatusCallback& callback);

// |access_token| is owned by caller and must exist before
// StartCheckAndroidManagement is called for testing.
static void SetAccessTokenForTesting(const char* access_token);

private:
// OAuth2TokenService::Consumer:
void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) override;
void OnGetTokenFailure(const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) override;

// Requests an access token.
void RequestAccessToken();

// Sends a CheckAndroidManagementRequest to DM server.
void CheckAndroidManagement(const std::string& access_token);

// Callback for check Android management requests.
void OnAndroidManagementChecked(
DeviceManagementStatus status,
int net_error,
const enterprise_management::DeviceManagementResponse& response);

// Used to communicate with the device management service.
DeviceManagementService* const device_management_service_;
scoped_refptr<net::URLRequestContextGetter> request_context_;
std::unique_ptr<DeviceManagementRequestJob> request_job_;

// The account ID that will be used for the access token fetch.
const std::string account_id_;
// The token service used to retrieve the access token.
OAuth2TokenService* const token_service_;
// The OAuth request to receive the access token.
std::unique_ptr<OAuth2TokenService::Request> token_request_;

StatusCallback callback_;

base::WeakPtrFactory<AndroidManagementClient> weak_ptr_factory_;

DISALLOW_COPY_AND_ASSIGN(AndroidManagementClient);
};

} // namespace policy

#endif // CHROME_BROWSER_CHROMEOS_POLICY_ANDROID_MANAGEMENT_CLIENT_H_
108 changes: 108 additions & 0 deletions chrome/browser/chromeos/policy/android_management_client_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/policy/android_management_client.h"
#include "components/policy/core/common/cloud/mock_device_management_service.h"
#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_test_util.h"
#include "policy/proto/device_management_backend.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::SaveArg;
using testing::StrictMock;
using testing::_;

namespace em = enterprise_management;

namespace policy {

namespace {

const char kAccountId[] = "fake-account-id";
const char kRefreshToken[] = "fake-refresh-token";
const char kOAuthToken[] = "fake-oauth-token";

MATCHER_P(MatchProto, expected, "matches protobuf") {
return arg.SerializePartialAsString() == expected.SerializePartialAsString();
}

// A mock class to allow us to set expectations on upload callbacks.
class MockStatusCallbackObserver {
public:
MockStatusCallbackObserver() {}

MOCK_METHOD1(OnCallbackComplete, void(AndroidManagementClient::Result));
};

} // namespace

class AndroidManagementClientTest : public testing::Test {
protected:
AndroidManagementClientTest() {
android_management_request_.mutable_check_android_management_request();
android_management_response_.mutable_check_android_management_response();
}

// testing::Test:
void SetUp() override {
request_context_ =
new net::TestURLRequestContextGetter(loop_.task_runner());
client_.reset(new AndroidManagementClient(&service_, request_context_,
kAccountId, &token_service_));
}

// Request protobuf is used as extectation for the client requests.
em::DeviceManagementRequest android_management_request_;

// Protobuf is used in successfil responsees.
em::DeviceManagementResponse android_management_response_;

base::MessageLoop loop_;
MockDeviceManagementService service_;
StrictMock<MockStatusCallbackObserver> callback_observer_;
std::unique_ptr<AndroidManagementClient> client_;
// Pointer to the client's request context.
scoped_refptr<net::URLRequestContextGetter> request_context_;
std::string oauh_token_;
FakeProfileOAuth2TokenService token_service_;
};

TEST_F(AndroidManagementClientTest, CheckAndroidManagementCall) {
std::string client_id;
EXPECT_CALL(
service_,
CreateJob(DeviceManagementRequestJob::TYPE_ANDROID_MANAGEMENT_CHECK,
request_context_))
.WillOnce(service_.SucceedJob(android_management_response_));
EXPECT_CALL(service_,
StartJob(dm_protocol::kValueRequestCheckAndroidManagement,
std::string(), kOAuthToken, std::string(), _,
MatchProto(android_management_request_)))
.WillOnce(SaveArg<4>(&client_id));
EXPECT_CALL(
callback_observer_,
OnCallbackComplete(AndroidManagementClient::Result::RESULT_UNMANAGED))
.Times(1);

AndroidManagementClient::StatusCallback callback =
base::Bind(&MockStatusCallbackObserver::OnCallbackComplete,
base::Unretained(&callback_observer_));

token_service_.UpdateCredentials(kAccountId, kRefreshToken);
client_->StartCheckAndroidManagement(callback);
token_service_.IssueAllTokensForAccount(kAccountId, kOAuthToken,
base::Time::Max());
ASSERT_LT(client_id.size(), 64U);
}

} // namespace policy
2 changes: 2 additions & 0 deletions chrome/chrome_browser_chromeos.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,8 @@
'browser/chromeos/policy/affiliated_invalidation_service_provider.h',
'browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc',
'browser/chromeos/policy/affiliated_invalidation_service_provider_impl.h',
'browser/chromeos/policy/android_management_client.cc',
'browser/chromeos/policy/android_management_client.h',
'browser/chromeos/policy/auto_enrollment_client.cc',
'browser/chromeos/policy/auto_enrollment_client.h',
'browser/chromeos/policy/bluetooth_policy_handler.cc',
Expand Down
1 change: 1 addition & 0 deletions chrome/chrome_tests_unit.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,7 @@
'browser/chromeos/ownership/owner_settings_service_chromeos_unittest.cc',
'browser/chromeos/policy/affiliated_cloud_policy_invalidator_unittest.cc',
'browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc',
'browser/chromeos/policy/android_management_client_unittest.cc',
'browser/chromeos/policy/auto_enrollment_client_unittest.cc',
'browser/chromeos/policy/cloud_external_data_manager_base_unittest.cc',
'browser/chromeos/policy/cloud_external_data_policy_observer_unittest.cc',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const char kValueRequestDeviceAttributeUpdatePermission[] =
"device_attribute_update_permission";
const char kValueRequestDeviceAttributeUpdate[] = "device_attribute_update";
const char kValueRequestGcmIdUpdate[] = "gcm_id_update";
const char kValueRequestCheckAndroidManagement[] = "check_android_management";

const char kChromeDevicePolicyType[] = "google/chromeos/device";
#if defined(OS_CHROMEOS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ POLICY_EXPORT extern const char kValueRequestRemoteCommands[];
POLICY_EXPORT extern const char kValueRequestDeviceAttributeUpdatePermission[];
POLICY_EXPORT extern const char kValueRequestDeviceAttributeUpdate[];
POLICY_EXPORT extern const char kValueRequestGcmIdUpdate[];
POLICY_EXPORT extern const char kValueRequestCheckAndroidManagement[];

// Policy type strings for the policy_type field in PolicyFetchRequest.
POLICY_EXPORT extern const char kChromeDevicePolicyType[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) {
return dm_protocol::kValueRequestDeviceAttributeUpdate;
case DeviceManagementRequestJob::TYPE_GCM_ID_UPDATE:
return dm_protocol::kValueRequestGcmIdUpdate;
case DeviceManagementRequestJob::TYPE_ANDROID_MANAGEMENT_CHECK:
return dm_protocol::kValueRequestCheckAndroidManagement;
}
NOTREACHED() << "Invalid job type " << type;
return "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class POLICY_EXPORT DeviceManagementRequestJob {
TYPE_ATTRIBUTE_UPDATE_PERMISSION,
TYPE_ATTRIBUTE_UPDATE,
TYPE_GCM_ID_UPDATE,
TYPE_ANDROID_MANAGEMENT_CHECK,
};

typedef base::Callback<
Expand Down
Loading

0 comments on commit bcd3a5f

Please sign in to comment.