diff options
author | gfeher@chromium.org <gfeher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-01 23:01:35 +0000 |
---|---|---|
committer | gfeher@chromium.org <gfeher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-01 23:01:35 +0000 |
commit | f38374f3cd28322208f71094ace8162299b0380d (patch) | |
tree | 0281324f99fab58bcbe109674044b0fb3334e6a4 | |
parent | 9100ab1858c6ac0ab8334215f9c13ec65dca7be4 (diff) | |
download | chromium_src-f38374f3cd28322208f71094ace8162299b0380d.zip chromium_src-f38374f3cd28322208f71094ace8162299b0380d.tar.gz chromium_src-f38374f3cd28322208f71094ace8162299b0380d.tar.bz2 |
Update policy backend and testserver for the latest policy protocol
BUG=chromium-os:11253, chromium-os:11254, chromium-os:11255
TEST=DeviceManagementServiceIntegrationTest.WithTestServer
Review URL: http://codereview.chromium.org/6537020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76455 0039d316-1c4b-4281-b951-d872f2087c98
26 files changed, 716 insertions, 523 deletions
diff --git a/chrome/browser/policy/cloud_policy_cache.cc b/chrome/browser/policy/cloud_policy_cache.cc index 445ff86..7b8a0fa 100644 --- a/chrome/browser/policy/cloud_policy_cache.cc +++ b/chrome/browser/policy/cloud_policy_cache.cc @@ -82,7 +82,7 @@ class CloudPolicyCache::CloudPolicyProvider class PersistPolicyTask : public Task { public: PersistPolicyTask(const FilePath& path, - const em::CloudPolicyResponse* cloud_policy_response, + const em::PolicyFetchResponse* cloud_policy_response, const em::DevicePolicyResponse* device_policy_response, const bool is_unmanaged) : path_(path), @@ -95,7 +95,7 @@ class PersistPolicyTask : public Task { virtual void Run(); const FilePath path_; - scoped_ptr<const em::CloudPolicyResponse> cloud_policy_response_; + scoped_ptr<const em::PolicyFetchResponse> cloud_policy_response_; scoped_ptr<const em::DevicePolicyResponse> device_policy_response_; const bool is_unmanaged_; }; @@ -211,7 +211,7 @@ void CloudPolicyCache::LoadFromFile() { observer_list_, OnUpdatePolicy()); } -void CloudPolicyCache::SetPolicy(const em::CloudPolicyResponse& policy) { +void CloudPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) { DCHECK(CalledOnValidThread()); bool initialization_was_not_complete = !initialization_complete_; is_unmanaged_ = false; @@ -242,7 +242,7 @@ void CloudPolicyCache::SetPolicy(const em::CloudPolicyResponse& policy) { LOG(WARNING) << "Server returned policy with timestamp from the future, " "not persisting to disk."; } else { - em::CloudPolicyResponse* policy_copy = new em::CloudPolicyResponse; + em::PolicyFetchResponse* policy_copy = new em::PolicyFetchResponse; policy_copy->CopyFrom(policy); BrowserThread::PostTask( BrowserThread::FILE, @@ -305,33 +305,38 @@ void CloudPolicyCache::SetUnmanaged() { // static bool CloudPolicyCache::DecodePolicyResponse( - const em::CloudPolicyResponse& policy_response, + const em::PolicyFetchResponse& policy_response, PolicyMap* mandatory, PolicyMap* recommended, base::Time* timestamp) { - std::string data = policy_response.signed_response(); + std::string data = policy_response.policy_data(); - if (!VerifySignature(policy_response.signature(), data, + if (!VerifySignature(policy_response.policy_data_signature(), data, policy_response.certificate_chain())) { LOG(WARNING) << "Failed to verify signature."; return false; } - em::SignedCloudPolicyResponse response; - if (!response.ParseFromArray(data.c_str(), data.size())) { - LOG(WARNING) << "Failed to parse SignedCloudPolicyResponse protobuf."; + em::PolicyData policy_data; + if (!policy_data.ParseFromString(data)) { + LOG(WARNING) << "Failed to parse PolicyData protobuf."; return false; } - // TODO(jkummerow): Verify response.device_token(). Needs final specification - // which token we're actually sending / expecting to get back. + // TODO(jkummerow): Verify policy_data.device_token(). Needs final + // specification which token we're actually sending / expecting to get back. - // TODO(jkummerow): Store response.device_name(), if we decide to transfer + // TODO(jkummerow): Store policy_data.device_name(), if we decide to transfer // it from the server to the client. - DCHECK(timestamp); - *timestamp = base::Time::FromTimeT(response.timestamp()); - DecodePolicy(response.settings(), mandatory, recommended); + *timestamp = base::Time::UnixEpoch() + + base::TimeDelta::FromMilliseconds(policy_data.timestamp()); + em::CloudPolicySettings policy; + if (!policy.ParseFromString(policy_data.policy_value())) { + LOG(WARNING) << "Failed to parse CloudPolicySettings protobuf."; + return false; + } + DecodePolicy(policy, mandatory, recommended); return true; } diff --git a/chrome/browser/policy/cloud_policy_cache.h b/chrome/browser/policy/cloud_policy_cache.h index 5e7745a..ce7965b 100644 --- a/chrome/browser/policy/cloud_policy_cache.h +++ b/chrome/browser/policy/cloud_policy_cache.h @@ -52,7 +52,7 @@ class CloudPolicyCache : public base::NonThreadSafe { void LoadFromFile(); // Resets the policy information. - void SetPolicy(const em::CloudPolicyResponse& policy); + void SetPolicy(const em::PolicyFetchResponse& policy); void SetDevicePolicy(const em::DevicePolicyResponse& policy); ConfigurationPolicyProvider* GetManagedPolicyProvider(); @@ -85,7 +85,7 @@ class CloudPolicyCache : public base::NonThreadSafe { // maps and a timestamp. Also performs verification, returns NULL if any // check fails. static bool DecodePolicyResponse( - const em::CloudPolicyResponse& policy_response, + const em::PolicyFetchResponse& policy_response, PolicyMap* mandatory, PolicyMap* recommended, base::Time* timestamp); diff --git a/chrome/browser/policy/cloud_policy_cache_unittest.cc b/chrome/browser/policy/cloud_policy_cache_unittest.cc index 54e1de0..7140ccb 100644 --- a/chrome/browser/policy/cloud_policy_cache_unittest.cc +++ b/chrome/browser/policy/cloud_policy_cache_unittest.cc @@ -58,36 +58,39 @@ class CloudPolicyCacheTest : public testing::Test { loop_.RunAllPending(); } - // Creates a (signed) CloudPolicyResponse setting the given |homepage| and + // Creates a (signed) PolicyFetchResponse setting the given |homepage| and // featuring the given |timestamp| (as issued by the server). // Mildly hacky special feature: pass an empty string as |homepage| to get // a completely empty policy. - em::CloudPolicyResponse* CreateHomepagePolicy( + em::PolicyFetchResponse* CreateHomepagePolicy( const std::string& homepage, const base::Time& timestamp, const em::PolicyOptions::PolicyMode policy_mode) { - em::SignedCloudPolicyResponse signed_response; + em::PolicyData signed_response; if (homepage != "") { - em::CloudPolicySettings* settings = signed_response.mutable_settings(); + em::CloudPolicySettings settings; em::HomepageLocationProto* homepagelocation_proto = - settings->mutable_homepagelocation(); + settings.mutable_homepagelocation(); homepagelocation_proto->set_homepagelocation(homepage); homepagelocation_proto->mutable_policy_options()->set_mode(policy_mode); + EXPECT_TRUE( + settings.SerializeToString(signed_response.mutable_policy_value())); } - signed_response.set_timestamp(timestamp.ToTimeT()); + signed_response.set_timestamp( + (timestamp - base::Time::UnixEpoch()).InMilliseconds()); std::string serialized_signed_response; EXPECT_TRUE(signed_response.SerializeToString(&serialized_signed_response)); - em::CloudPolicyResponse* response = new em::CloudPolicyResponse; - response->set_signed_response(serialized_signed_response); + em::PolicyFetchResponse* response = new em::PolicyFetchResponse; + response->set_policy_data(serialized_signed_response); // TODO(jkummerow): Set proper certificate_chain and signature (when // implementing support for signature verification). - response->set_signature("TODO"); + response->set_policy_data_signature("TODO"); response->add_certificate_chain("TODO"); return response; } - void WritePolicy(const em::CloudPolicyResponse& policy) { + void WritePolicy(const em::PolicyFetchResponse& policy) { std::string data; em::CachedCloudPolicyResponse cached_policy; cached_policy.mutable_cloud_policy()->CopyFrom(policy); @@ -98,9 +101,9 @@ class CloudPolicyCacheTest : public testing::Test { // Takes ownership of |policy_response|. void SetPolicy(CloudPolicyCache* cache, - em::CloudPolicyResponse* policy_response, + em::PolicyFetchResponse* policy_response, bool expect_changed_policy) { - scoped_ptr<em::CloudPolicyResponse> policy(policy_response); + scoped_ptr<em::PolicyFetchResponse> policy(policy_response); ConfigurationPolicyObserverRegistrar registrar; registrar.Init(cache->GetManagedPolicyProvider(), &observer); if (expect_changed_policy) @@ -206,7 +209,7 @@ TEST_F(CloudPolicyCacheTest, LoadNoFile) { } TEST_F(CloudPolicyCacheTest, RejectFuture) { - scoped_ptr<em::CloudPolicyResponse> policy_response( + scoped_ptr<em::PolicyFetchResponse> policy_response( CreateHomepagePolicy("", base::Time::NowFromSystemTime() + base::TimeDelta::FromMinutes(5), em::PolicyOptions::MANDATORY)); @@ -219,7 +222,7 @@ TEST_F(CloudPolicyCacheTest, RejectFuture) { } TEST_F(CloudPolicyCacheTest, LoadWithFile) { - scoped_ptr<em::CloudPolicyResponse> policy_response( + scoped_ptr<em::PolicyFetchResponse> policy_response( CreateHomepagePolicy("", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY)); WritePolicy(*policy_response); @@ -232,7 +235,7 @@ TEST_F(CloudPolicyCacheTest, LoadWithFile) { } TEST_F(CloudPolicyCacheTest, LoadWithData) { - scoped_ptr<em::CloudPolicyResponse> policy( + scoped_ptr<em::PolicyFetchResponse> policy( CreateHomepagePolicy("http://www.example.com", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY)); @@ -247,12 +250,12 @@ TEST_F(CloudPolicyCacheTest, LoadWithData) { TEST_F(CloudPolicyCacheTest, SetPolicy) { CloudPolicyCache cache(test_file()); - em::CloudPolicyResponse* policy = + em::PolicyFetchResponse* policy = CreateHomepagePolicy("http://www.example.com", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY); SetPolicy(&cache, policy, true); - em::CloudPolicyResponse* policy2 = + em::PolicyFetchResponse* policy2 = CreateHomepagePolicy("http://www.example.com", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY); @@ -264,8 +267,8 @@ TEST_F(CloudPolicyCacheTest, SetPolicy) { EXPECT_TRUE(expected.Equals(mandatory_policy(cache))); EXPECT_TRUE(empty.Equals(recommended_policy(cache))); policy = CreateHomepagePolicy("http://www.example.com", - base::Time::NowFromSystemTime(), - em::PolicyOptions::RECOMMENDED); + base::Time::NowFromSystemTime(), + em::PolicyOptions::RECOMMENDED); SetPolicy(&cache, policy, true); EXPECT_TRUE(expected.Equals(recommended_policy(cache))); EXPECT_TRUE(empty.Equals(mandatory_policy(cache))); @@ -274,7 +277,7 @@ TEST_F(CloudPolicyCacheTest, SetPolicy) { TEST_F(CloudPolicyCacheTest, ResetPolicy) { CloudPolicyCache cache(test_file()); - em::CloudPolicyResponse* policy = + em::PolicyFetchResponse* policy = CreateHomepagePolicy("http://www.example.com", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY); @@ -284,7 +287,7 @@ TEST_F(CloudPolicyCacheTest, ResetPolicy) { Value::CreateStringValue("http://www.example.com")); EXPECT_TRUE(expected.Equals(mandatory_policy(cache))); - em::CloudPolicyResponse* empty_policy = + em::PolicyFetchResponse* empty_policy = CreateHomepagePolicy("", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY); SetPolicy(&cache, empty_policy, true); @@ -295,7 +298,7 @@ TEST_F(CloudPolicyCacheTest, ResetPolicy) { TEST_F(CloudPolicyCacheTest, PersistPolicy) { { CloudPolicyCache cache(test_file()); - scoped_ptr<em::CloudPolicyResponse> policy( + scoped_ptr<em::PolicyFetchResponse> policy( CreateHomepagePolicy("http://www.example.com", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY)); @@ -314,14 +317,14 @@ TEST_F(CloudPolicyCacheTest, PersistPolicy) { } TEST_F(CloudPolicyCacheTest, FreshPolicyOverride) { - scoped_ptr<em::CloudPolicyResponse> policy( + scoped_ptr<em::PolicyFetchResponse> policy( CreateHomepagePolicy("http://www.example.com", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY)); WritePolicy(*policy); CloudPolicyCache cache(test_file()); - em::CloudPolicyResponse* updated_policy = + em::PolicyFetchResponse* updated_policy = CreateHomepagePolicy("http://www.chromium.org", base::Time::NowFromSystemTime(), em::PolicyOptions::MANDATORY); diff --git a/chrome/browser/policy/cloud_policy_controller.cc b/chrome/browser/policy/cloud_policy_controller.cc index 7ace406..e9b61f0 100644 --- a/chrome/browser/policy/cloud_policy_controller.cc +++ b/chrome/browser/policy/cloud_policy_controller.cc @@ -93,17 +93,18 @@ void CloudPolicyController::HandlePolicyResponse( if (state_ == STATE_TOKEN_UNAVAILABLE) return; - cache_->SetDevicePolicy(response); - SetState(STATE_POLICY_VALID); -} - -void CloudPolicyController::HandleCloudPolicyResponse( - const em::CloudPolicyResponse& response) { - if (state_ == STATE_TOKEN_UNAVAILABLE) - return; - - cache_->SetPolicy(response); - SetState(STATE_POLICY_VALID); + if (response.response_size() > 0) { + if (response.response_size() > 1) { + LOG(WARNING) << "More than one policy in the response of the device " + << "management server, discarding."; + } + // Use the new version of the protocol + cache_->SetPolicy(response.response(0)); + SetState(STATE_POLICY_VALID); + } else { + cache_->SetDevicePolicy(response); + SetState(STATE_POLICY_VALID); + } } void CloudPolicyController::OnError(DeviceManagementBackend::ErrorCode code) { @@ -119,17 +120,7 @@ void CloudPolicyController::OnError(DeviceManagementBackend::ErrorCode code) { DeviceManagementBackend::kErrorServiceManagementNotSupported) { VLOG(1) << "The device is no longer managed, resetting device token."; SetState(STATE_TOKEN_UNAVAILABLE); - } else if (!fallback_to_old_protocol_ && - code == DeviceManagementBackend::kErrorRequestInvalid) { - LOG(WARNING) << "Device manager doesn't understand new protocol, falling " - << "back to old request."; - fallback_to_old_protocol_ = true; - SetState(STATE_TOKEN_VALID); // Triggers SendPolicyRequest() immediately. } else { - LOG(WARNING) << "Could not provide policy from the device manager (error = " - << code << "), will retry in " - << (effective_policy_refresh_error_delay_ms_ / 1000) - << " seconds."; SetState(STATE_POLICY_ERROR); } } @@ -185,7 +176,6 @@ void CloudPolicyController::Initialize( token_fetcher_ = token_fetcher; identity_strategy_ = identity_strategy; state_ = STATE_TOKEN_UNAVAILABLE; - fallback_to_old_protocol_ = false; delayed_work_task_ = NULL; policy_refresh_rate_ms_ = policy_refresh_rate_ms; policy_refresh_deviation_factor_percent_ = @@ -206,31 +196,39 @@ void CloudPolicyController::FetchToken() { std::string username; std::string auth_token; std::string device_id = identity_strategy_->GetDeviceID(); + std::string machine_id = identity_strategy_->GetMachineID(); + em::DeviceRegisterRequest_Type policy_type = + identity_strategy_->GetPolicyRegisterType(); if (identity_strategy_->GetCredentials(&username, &auth_token) && CanBeInManagedDomain(username)) { - token_fetcher_->FetchToken(auth_token, device_id); + token_fetcher_->FetchToken(auth_token, device_id, policy_type, machine_id); } } void CloudPolicyController::SendPolicyRequest() { DCHECK(!identity_strategy_->GetDeviceToken().empty()); - if (!fallback_to_old_protocol_) { - em::CloudPolicyRequest policy_request; - policy_request.set_policy_scope(kChromePolicyScope); - backend_->ProcessCloudPolicyRequest(identity_strategy_->GetDeviceToken(), - identity_strategy_->GetDeviceID(), - policy_request, this); - } else { - em::DevicePolicyRequest policy_request; - policy_request.set_policy_scope(kChromePolicyScope); - em::DevicePolicySettingRequest* setting = - policy_request.add_setting_request(); - setting->set_key(kChromeDevicePolicySettingKey); - setting->set_watermark(""); - backend_->ProcessPolicyRequest(identity_strategy_->GetDeviceToken(), - identity_strategy_->GetDeviceID(), - policy_request, this); + em::DevicePolicyRequest policy_request; + em::PolicyFetchRequest* fetch_request = policy_request.add_request(); + fetch_request->set_signature_type(em::PolicyFetchRequest::X509); + fetch_request->set_policy_type(identity_strategy_->GetPolicyType()); + if (!cache_->is_unmanaged() && + !cache_->last_policy_refresh_time().is_null()) { + base::TimeDelta timestamp = + cache_->last_policy_refresh_time() - base::Time::UnixEpoch(); + fetch_request->set_timestamp(timestamp.InMilliseconds()); } + + // TODO(gfeher): Remove the following block when the server is migrated. + // Set fields for the old protocol. + policy_request.set_policy_scope(kChromePolicyScope); + em::DevicePolicySettingRequest* setting = + policy_request.add_setting_request(); + setting->set_key(kChromeDevicePolicySettingKey); + setting->set_watermark(""); + + backend_->ProcessPolicyRequest(identity_strategy_->GetDeviceToken(), + identity_strategy_->GetDeviceID(), + policy_request, this); } void CloudPolicyController::DoDelayedWork() { diff --git a/chrome/browser/policy/cloud_policy_controller.h b/chrome/browser/policy/cloud_policy_controller.h index 3375981..42bb1eb 100644 --- a/chrome/browser/policy/cloud_policy_controller.h +++ b/chrome/browser/policy/cloud_policy_controller.h @@ -47,8 +47,6 @@ class CloudPolicyController // DevicePolicyResponseDelegate implementation: virtual void HandlePolicyResponse( const em::DevicePolicyResponse& response); - virtual void HandleCloudPolicyResponse( - const em::CloudPolicyResponse& response); virtual void OnError(DeviceManagementBackend::ErrorCode code); // DeviceTokenFetcher::Observer implementation: @@ -119,7 +117,6 @@ class CloudPolicyController DeviceTokenFetcher* token_fetcher_; ControllerState state_; bool initial_fetch_done_; - bool fallback_to_old_protocol_; int64 policy_refresh_rate_ms_; int policy_refresh_deviation_factor_percent_; diff --git a/chrome/browser/policy/cloud_policy_controller_unittest.cc b/chrome/browser/policy/cloud_policy_controller_unittest.cc index 64d8408..66d872d 100644 --- a/chrome/browser/policy/cloud_policy_controller_unittest.cc +++ b/chrome/browser/policy/cloud_policy_controller_unittest.cc @@ -7,6 +7,8 @@ #include "base/message_loop.h" #include "base/scoped_temp_dir.h" #include "chrome/browser/policy/cloud_policy_cache.h" +#include "chrome/browser/policy/device_token_fetcher.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" #include "chrome/browser/policy/mock_configuration_policy_store.h" #include "chrome/browser/policy/mock_device_management_backend.h" #include "content/browser/browser_thread.h" @@ -18,6 +20,8 @@ const char kTestToken[] = "cloud_policy_controller_test_auth_token"; namespace policy { +namespace em = enterprise_management; + using ::testing::_; using ::testing::AtLeast; using ::testing::InSequence; @@ -31,6 +35,10 @@ class MockCloudPolicyIdentityStrategy : public CloudPolicyIdentityStrategy { MOCK_METHOD0(GetDeviceToken, std::string()); MOCK_METHOD0(GetDeviceID, std::string()); + MOCK_METHOD0(GetMachineID, std::string()); + MOCK_METHOD0(GetPolicyType, std::string()); + MOCK_METHOD0(GetPolicyRegisterType, em::DeviceRegisterRequest_Type()); + MOCK_METHOD2(GetCredentials, bool(std::string*, std::string*)); virtual void OnDeviceTokenAvailable(const std::string&) {} @@ -50,8 +58,10 @@ class MockDeviceTokenFetcher : public DeviceTokenFetcher { : DeviceTokenFetcher(NULL, cache) {} virtual ~MockDeviceTokenFetcher() {} - MOCK_METHOD0(GetDeviceToken, std::string&()); - MOCK_METHOD2(FetchToken, void(const std::string&, const std::string&)); + MOCK_METHOD0(GetDeviceToken, const std::string&()); + MOCK_METHOD4(FetchToken, + void(const std::string&, const std::string&, + em::DeviceRegisterRequest_Type, const std::string&)); private: DISALLOW_COPY_AND_ASSIGN(MockDeviceTokenFetcher); @@ -104,14 +114,24 @@ class CloudPolicyControllerTest : public testing::Test { EXPECT_TRUE(store.Get(kPolicyDisableSpdy)->Equals(&expected)); } - void SetupIdentityStrategy(const std::string& device_token, - const std::string& device_id, - const std::string& user_name, - const std::string& auth_token) { + void SetupIdentityStrategy( + const std::string& device_token, + const std::string& device_id, + const std::string& machine_id, + const std::string& policy_type, + const em::DeviceRegisterRequest_Type& policy_register_type, + const std::string& user_name, + const std::string& auth_token) { EXPECT_CALL(identity_strategy_, GetDeviceToken()).WillRepeatedly( Return(device_token)); EXPECT_CALL(identity_strategy_, GetDeviceID()).WillRepeatedly( Return(device_id)); + EXPECT_CALL(identity_strategy_, GetMachineID()).WillRepeatedly( + Return(machine_id)); + EXPECT_CALL(identity_strategy_, GetPolicyType()).WillRepeatedly( + Return(policy_type)); + EXPECT_CALL(identity_strategy_, GetPolicyRegisterType()).WillRepeatedly( + Return(policy_register_type)); if (!user_name.empty()) { EXPECT_CALL(identity_strategy_, GetCredentials(_, _)).WillRepeatedly( MockCloudPolicyIdentityStrategyGetCredentials(user_name, auth_token)); @@ -136,9 +156,11 @@ class CloudPolicyControllerTest : public testing::Test { // If a device token is present when the controller starts up, it should // fetch and apply policy. TEST_F(CloudPolicyControllerTest, StartupWithDeviceToken) { - SetupIdentityStrategy("fake_device_token", "device_id", "", ""); + SetupIdentityStrategy("fake_device_token", "device_id", "machine_id", + "google/chromeos/user", + em::DeviceRegisterRequest::USER, "", ""); MockDeviceManagementBackend* backend = new MockDeviceManagementBackend(); - EXPECT_CALL(*backend, ProcessCloudPolicyRequest(_, _, _, _)).WillOnce( + EXPECT_CALL(*backend, ProcessPolicyRequest(_, _, _, _)).WillOnce( MockDeviceManagementBackendSucceedSpdyCloudPolicy()); CreateNewController(backend); loop_.RunAllPending(); @@ -148,8 +170,10 @@ TEST_F(CloudPolicyControllerTest, StartupWithDeviceToken) { // If no device token is present when the controller starts up, it should // instruct the token_fetcher_ to fetch one. TEST_F(CloudPolicyControllerTest, StartupWithoutDeviceToken) { - SetupIdentityStrategy("", "device_id", "a@b.com", "auth_token"); - EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _)).Times(1); + SetupIdentityStrategy("", "device_id", "machine_id", "google/chromeos/user", + em::DeviceRegisterRequest::USER, + "a@b.com", "auth_token"); + EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _)).Times(1); CreateNewController(NULL); loop_.RunAllPending(); } @@ -157,8 +181,10 @@ TEST_F(CloudPolicyControllerTest, StartupWithoutDeviceToken) { // If the current user belongs to a known non-managed domain, no token fetch // should be initiated. TEST_F(CloudPolicyControllerTest, StartupUnmanagedUser) { - SetupIdentityStrategy("", "device_id", "DannoHelper@gmail.com", "auth_token"); - EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _)).Times(0); + SetupIdentityStrategy("", "device_id", "machine_id", "google/chromeos/user", + em::DeviceRegisterRequest::USER, + "DannoHelper@gmail.com", "auth_token"); + EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _)).Times(0); CreateNewController(NULL); loop_.RunAllPending(); } @@ -166,10 +192,11 @@ TEST_F(CloudPolicyControllerTest, StartupUnmanagedUser) { // After policy has been fetched successfully, a new fetch should be triggered // after the refresh interval has timed out. TEST_F(CloudPolicyControllerTest, RefreshAfterSuccessfulPolicy) { - SetupIdentityStrategy("device_token", "device_id", + SetupIdentityStrategy("device_token", "device_id", "machine_id", + "google/chromeos/user", em::DeviceRegisterRequest::USER, "DannoHelperDelegate@b.com", "auth_token"); MockDeviceManagementBackend* backend = new MockDeviceManagementBackend(); - EXPECT_CALL(*backend, ProcessCloudPolicyRequest(_, _, _, _)).WillOnce( + EXPECT_CALL(*backend, ProcessPolicyRequest(_, _, _, _)).WillOnce( MockDeviceManagementBackendSucceedSpdyCloudPolicy()).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorRequestFailed)); @@ -180,10 +207,11 @@ TEST_F(CloudPolicyControllerTest, RefreshAfterSuccessfulPolicy) { // If poliy fetching failed, it should be retried. TEST_F(CloudPolicyControllerTest, RefreshAfterError) { - SetupIdentityStrategy("device_token", "device_id", + SetupIdentityStrategy("device_token", "device_id", "machine_id", + "google/chromeos/user", em::DeviceRegisterRequest::USER, "DannoHelperDelegateImpl@b.com", "auth_token"); MockDeviceManagementBackend* backend = new MockDeviceManagementBackend(); - EXPECT_CALL(*backend, ProcessCloudPolicyRequest(_, _, _, _)).WillOnce( + EXPECT_CALL(*backend, ProcessPolicyRequest(_, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorRequestFailed)).WillOnce( MockDeviceManagementBackendSucceedSpdyCloudPolicy()); @@ -195,12 +223,14 @@ TEST_F(CloudPolicyControllerTest, RefreshAfterError) { // If the backend reports that the device token was invalid, the controller // should instruct the token fetcher to fetch a new token. TEST_F(CloudPolicyControllerTest, InvalidToken) { - SetupIdentityStrategy("device_token", "device_id", "standup@ten.am", "auth"); + SetupIdentityStrategy("device_token", "device_id", "machine_id", + "google/chromeos/user", em::DeviceRegisterRequest::USER, + "standup@ten.am", "auth"); MockDeviceManagementBackend* backend = new MockDeviceManagementBackend(); - EXPECT_CALL(*backend, ProcessCloudPolicyRequest(_, _, _, _)).WillOnce( + EXPECT_CALL(*backend, ProcessPolicyRequest(_, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorServiceManagementTokenInvalid)); - EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _)).Times(1); + EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _)).Times(1); CreateNewController(backend); loop_.RunAllPending(); } @@ -208,12 +238,14 @@ TEST_F(CloudPolicyControllerTest, InvalidToken) { // If the backend reports that the device is unknown to the server, the // controller should instruct the token fetcher to fetch a new token. TEST_F(CloudPolicyControllerTest, DeviceNotFound) { - SetupIdentityStrategy("device_token", "device_id", "me@you.com", "auth"); + SetupIdentityStrategy("device_token", "device_id", "machine_id", + "google/chromeos/user", em::DeviceRegisterRequest::USER, + "me@you.com", "auth"); MockDeviceManagementBackend* backend = new MockDeviceManagementBackend(); - EXPECT_CALL(*backend, ProcessCloudPolicyRequest(_, _, _, _)).WillOnce( + EXPECT_CALL(*backend, ProcessPolicyRequest(_, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorServiceDeviceNotFound)); - EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _)).Times(1); + EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _)).Times(1); CreateNewController(backend); loop_.RunAllPending(); } @@ -222,37 +254,16 @@ TEST_F(CloudPolicyControllerTest, DeviceNotFound) { // shoud instruct the token fetcher to fetch a new token (which will in turn // set and persist the correct 'unmanaged' state). TEST_F(CloudPolicyControllerTest, NoLongerManaged) { - SetupIdentityStrategy("device_token", "device_id", "who@what.com", "auth"); + SetupIdentityStrategy("device_token", "device_id", "machine_id", + "google/chromeos/user", em::DeviceRegisterRequest::USER, + "who@what.com", "auth"); MockDeviceManagementBackend* backend = new MockDeviceManagementBackend(); - EXPECT_CALL(*backend, ProcessCloudPolicyRequest(_, _, _, _)).WillOnce( + EXPECT_CALL(*backend, ProcessPolicyRequest(_, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorServiceManagementNotSupported)); - EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _)).Times(1); + EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _)).Times(1); CreateNewController(backend); loop_.RunAllPending(); } -// If the server doesn't support the new protocol, the controller should fall -// back to the old protocol. -TEST_F(CloudPolicyControllerTest, FallbackToOldProtocol) { - SetupIdentityStrategy("device_token", "device_id", "a@b.com", "auth"); - MockDeviceManagementBackend* backend = new MockDeviceManagementBackend(); - // If the CloudPolicyRequest fails with kErrorRequestInvalid... - EXPECT_CALL(*backend, ProcessCloudPolicyRequest(_, _, _, _)).WillOnce( - MockDeviceManagementBackendFailPolicy( - DeviceManagementBackend::kErrorRequestInvalid)); - // ...the client should fall back to a classic PolicyRequest, - // and remember this fallback for any future request, - // both after successful fetches and after errors. - EXPECT_CALL(*backend, ProcessPolicyRequest(_, _, _, _)).WillOnce( - MockDeviceManagementBackendSucceedBooleanPolicy( - key::kDisableSpdy, true)).WillOnce( - MockDeviceManagementBackendFailPolicy( - DeviceManagementBackend::kErrorHttpStatus)).WillOnce( - Return()); - CreateNewController(backend, 0, 0, 0, 0); - loop_.RunAllPending(); - ExpectHasSpdyPolicy(); -} - } // namespace policy diff --git a/chrome/browser/policy/cloud_policy_identity_strategy.h b/chrome/browser/policy/cloud_policy_identity_strategy.h index 1ecf840..1b4db76 100644 --- a/chrome/browser/policy/cloud_policy_identity_strategy.h +++ b/chrome/browser/policy/cloud_policy_identity_strategy.h @@ -9,9 +9,12 @@ #include <string> #include "base/observer_list.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" namespace policy { +namespace em = enterprise_management; + // Manages a device management token, i.e. an identifier that represents a // registration with the device management service, and the associated // credentials. Responsibilities include storing and loading the token from @@ -41,9 +44,22 @@ class CloudPolicyIdentityStrategy { // if the device token is currently unavailable. virtual std::string GetDeviceToken() = 0; - // Returns the device ID for this device. + // Returns the device ID for this device. This is a unique identifier that is + // randomly generated at registration time on the client side. It always has + // to be sent along with the device token to the server. virtual std::string GetDeviceID() = 0; + // Returns physical machine ID for this device. + virtual std::string GetMachineID() = 0; + + // Returns the policy type to be used for registering at the device management + // server. + virtual em::DeviceRegisterRequest_Type GetPolicyRegisterType() = 0; + + // Returns the policy type to be used for requesting policies from the device + // management server. + virtual std::string GetPolicyType() = 0; + // Retrieves authentication credentials to use when talking to the device // management service. Returns true if the data is available and writes the // values to the provided pointers. diff --git a/chrome/browser/policy/device_management_backend.h b/chrome/browser/policy/device_management_backend.h index f2fec0d..9a02b48 100644 --- a/chrome/browser/policy/device_management_backend.h +++ b/chrome/browser/policy/device_management_backend.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -75,12 +75,8 @@ class DeviceManagementBackend : base::NonThreadSafe { public: virtual ~DevicePolicyResponseDelegate() {} - // Deprecated in favor of HandleCloudPolicyResponse. To be removed once - // DMServer supports the new protocol. virtual void HandlePolicyResponse( const em::DevicePolicyResponse& response) = 0; - virtual void HandleCloudPolicyResponse( - const em::CloudPolicyResponse& response) = 0; virtual void OnError(ErrorCode code) = 0; protected: @@ -110,12 +106,6 @@ class DeviceManagementBackend : base::NonThreadSafe { const em::DevicePolicyRequest& request, DevicePolicyResponseDelegate* delegate) = 0; - virtual void ProcessCloudPolicyRequest( - const std::string& device_management_token, - const std::string& device_id, - const em::CloudPolicyRequest& request, - DevicePolicyResponseDelegate* delegate) = 0; - protected: DeviceManagementBackend() {} diff --git a/chrome/browser/policy/device_management_backend_impl.cc b/chrome/browser/policy/device_management_backend_impl.cc index b01cbf8..a964a28 100644 --- a/chrome/browser/policy/device_management_backend_impl.cc +++ b/chrome/browser/policy/device_management_backend_impl.cc @@ -27,14 +27,12 @@ const char DeviceManagementBackendImpl::kValueRequestRegister[] = "register"; const char DeviceManagementBackendImpl::kValueRequestUnregister[] = "unregister"; const char DeviceManagementBackendImpl::kValueRequestPolicy[] = "policy"; -const char DeviceManagementBackendImpl::kValueRequestCloudPolicy[] = - "cloud_policy"; -const char DeviceManagementBackendImpl::kValueDeviceType[] = "Chrome OS"; +const char DeviceManagementBackendImpl::kValueDeviceType[] = "2"; const char DeviceManagementBackendImpl::kValueAppType[] = "Chrome"; namespace { -const char kValueAgent[] = "%s enterprise management client version %s (%s)"; +const char kValueAgent[] = "%s enterprise management client %s (%s)"; const char kPostContentType[] = "application/protobuf"; @@ -332,41 +330,6 @@ class DeviceManagementPolicyJob : public DeviceManagementJobBase { DISALLOW_COPY_AND_ASSIGN(DeviceManagementPolicyJob); }; -// Handles cloud policy request jobs. -class CloudPolicyJob : public DeviceManagementJobBase { - public: - CloudPolicyJob( - DeviceManagementBackendImpl* backend_impl, - const std::string& device_management_token, - const std::string& device_id, - const em::CloudPolicyRequest& request, - DeviceManagementBackend::DevicePolicyResponseDelegate* delegate) - : DeviceManagementJobBase( - backend_impl, - DeviceManagementBackendImpl::kValueRequestCloudPolicy, - device_id), - delegate_(delegate) { - SetDeviceManagementToken(device_management_token); - em::DeviceManagementRequest request_wrapper; - request_wrapper.mutable_cloud_policy_request()->CopyFrom(request); - SetPayload(request_wrapper); - } - virtual ~CloudPolicyJob() {} - - private: - // DeviceManagementJobBase overrides. - virtual void OnError(DeviceManagementBackend::ErrorCode error) { - delegate_->OnError(error); - } - virtual void OnResponse(const em::DeviceManagementResponse& response) { - delegate_->HandleCloudPolicyResponse(response.cloud_policy_response()); - } - - DeviceManagementBackend::DevicePolicyResponseDelegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(CloudPolicyJob); -}; - DeviceManagementBackendImpl::DeviceManagementBackendImpl( DeviceManagementService* service) : service_(service) { @@ -428,13 +391,4 @@ void DeviceManagementBackendImpl::ProcessPolicyRequest( request, delegate)); } -void DeviceManagementBackendImpl::ProcessCloudPolicyRequest( - const std::string& device_management_token, - const std::string& device_id, - const em::CloudPolicyRequest& request, - DevicePolicyResponseDelegate* delegate) { - AddJob(new CloudPolicyJob(this, device_management_token, device_id, - request, delegate)); -} - } // namespace policy diff --git a/chrome/browser/policy/device_management_backend_impl.h b/chrome/browser/policy/device_management_backend_impl.h index 40c7e11..345bc40 100644 --- a/chrome/browser/policy/device_management_backend_impl.h +++ b/chrome/browser/policy/device_management_backend_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -36,10 +36,7 @@ class DeviceManagementBackendImpl : public DeviceManagementBackend { // String constants for the device and app type we report to the server. static const char kValueRequestRegister[]; static const char kValueRequestUnregister[]; - // Deprecated in favor of kValueRequestCloudPolicy. - // See DevicePolicyResponseDelegate::HandlePolicyResponse. static const char kValueRequestPolicy[]; - static const char kValueRequestCloudPolicy[]; static const char kValueDeviceType[]; static const char kValueAppType[]; @@ -66,18 +63,11 @@ class DeviceManagementBackendImpl : public DeviceManagementBackend { const std::string& device_id, const em::DeviceUnregisterRequest& request, DeviceUnregisterResponseDelegate* response_delegate); - // Deprecated in favor of ProcessCloudPolicyRequest. - // See DevicePolicyResponseDelegate::HandlePolicyResponse. virtual void ProcessPolicyRequest( const std::string& device_management_token, const std::string& device_id, const em::DevicePolicyRequest& request, DevicePolicyResponseDelegate* response_delegate); - virtual void ProcessCloudPolicyRequest( - const std::string& device_management_token, - const std::string& device_id, - const em::CloudPolicyRequest& request, - DevicePolicyResponseDelegate* delegate); // Keeps track of the jobs currently in flight. JobSet pending_jobs_; diff --git a/chrome/browser/policy/device_management_backend_mock.h b/chrome/browser/policy/device_management_backend_mock.h index 01ac14d..0533efc 100644 --- a/chrome/browser/policy/device_management_backend_mock.h +++ b/chrome/browser/policy/device_management_backend_mock.h @@ -40,7 +40,6 @@ class DevicePolicyResponseDelegateMock virtual ~DevicePolicyResponseDelegateMock(); MOCK_METHOD1(HandlePolicyResponse, void(const em::DevicePolicyResponse&)); - MOCK_METHOD1(HandleCloudPolicyResponse, void(const em::CloudPolicyResponse&)); MOCK_METHOD1(OnError, void(DeviceManagementBackend::ErrorCode error)); }; diff --git a/chrome/browser/policy/device_policy_identity_strategy.cc b/chrome/browser/policy/device_policy_identity_strategy.cc index 0f19945..42626a3 100644 --- a/chrome/browser/policy/device_policy_identity_strategy.cc +++ b/chrome/browser/policy/device_policy_identity_strategy.cc @@ -8,16 +8,79 @@ #include "chrome/browser/chromeos/login/ownership_service.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/policy/proto/device_management_constants.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/common/guid.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" namespace policy { +// Responsible for querying device ownership on the FILE thread. +class DevicePolicyIdentityStrategy::OwnershipChecker + : public base::RefCountedThreadSafe< + DevicePolicyIdentityStrategy::OwnershipChecker> { + public: + explicit OwnershipChecker( + const base::WeakPtr<DevicePolicyIdentityStrategy>& strategy) + : strategy_(strategy) {} + + // Initiates a query on the file thread to check if the currently logged in + // user is the owner. + void CheckCurrentUserIsOwner(); + + private: + void CheckOnFileThread(); + void CallbackOnUIThread(bool current_user_is_owner); + + private: + friend class base::RefCountedThreadSafe<OwnershipChecker>; + + ~OwnershipChecker() {} + + // The object to be called back with the result. + base::WeakPtr<DevicePolicyIdentityStrategy> strategy_; + + DISALLOW_COPY_AND_ASSIGN(OwnershipChecker); +}; + +void DevicePolicyIdentityStrategy::OwnershipChecker::CheckCurrentUserIsOwner() { + if (!strategy_.get()) + return; + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + NewRunnableMethod( + this, + &DevicePolicyIdentityStrategy::OwnershipChecker::CheckOnFileThread)); +} + +void DevicePolicyIdentityStrategy::OwnershipChecker::CheckOnFileThread() { + bool current_user_is_owner = + chromeos::OwnershipService::GetSharedInstance()->CurrentUserIsOwner(); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + NewRunnableMethod( + this, + &DevicePolicyIdentityStrategy::OwnershipChecker::CallbackOnUIThread, + current_user_is_owner)); +} + +void DevicePolicyIdentityStrategy::OwnershipChecker::CallbackOnUIThread( + bool current_user_is_owner) { + if (strategy_.get()) { + strategy_->OnOwnershipInformationAvailable(current_user_is_owner); + strategy_.reset(); + } +} + DevicePolicyIdentityStrategy::DevicePolicyIdentityStrategy() - : should_register_(false) { + : current_user_is_owner_(false), + ownership_checker_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { registrar_.Add(this, NotificationType::TOKEN_AVAILABLE, NotificationService::AllSources()); @@ -25,9 +88,6 @@ DevicePolicyIdentityStrategy::DevicePolicyIdentityStrategy() NotificationType::LOGIN_USER_CHANGED, NotificationService::AllSources()); registrar_.Add(this, - NotificationType::OWNERSHIP_TAKEN, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED, NotificationService::AllSources()); @@ -35,27 +95,58 @@ DevicePolicyIdentityStrategy::DevicePolicyIdentityStrategy() machine_id_ = "dummy-cros-machine-ID"; } +DevicePolicyIdentityStrategy::~DevicePolicyIdentityStrategy() { +} + +void DevicePolicyIdentityStrategy::OnOwnershipInformationAvailable( + bool current_user_is_owner) { + current_user_is_owner_ = current_user_is_owner; + CheckAndTriggerFetch(); +} + +void DevicePolicyIdentityStrategy::CheckOwnershipAndTriggerFetch() { + // TODO(gfeher): Avoid firing a new query if the answer is already known. + + // Cancel any pending queries. + weak_ptr_factory_.InvalidateWeakPtrs(); + // Set to false until we know that the current user is the owner. + current_user_is_owner_ = false; + // Issue a new query. + ownership_checker_ = new OwnershipChecker(weak_ptr_factory_.GetWeakPtr()); + // The following will call back to CheckTriggerFetch(). + ownership_checker_->CheckCurrentUserIsOwner(); +} + std::string DevicePolicyIdentityStrategy::GetDeviceToken() { return device_token_; } std::string DevicePolicyIdentityStrategy::GetDeviceID() { + return device_id_; +} + +std::string DevicePolicyIdentityStrategy::GetMachineID() { return machine_id_; } +em::DeviceRegisterRequest_Type +DevicePolicyIdentityStrategy::GetPolicyRegisterType() { + return em::DeviceRegisterRequest::DEVICE; +} + +std::string DevicePolicyIdentityStrategy::GetPolicyType() { + return kChromeDevicePolicyType; +} + bool DevicePolicyIdentityStrategy::GetCredentials(std::string* username, std::string* auth_token) { - // Only register if requested. - if (!should_register_) - return false; - // Need to know the machine id. if (machine_id_.empty()) return false; // Only fetch credentials (and, subsequently, token/policy) when the owner // is logged in. - if (!chromeos::OwnershipService::GetSharedInstance()->CurrentUserIsOwner()) + if (!current_user_is_owner_) return false; // We need to know about the profile of the logged in user. @@ -76,9 +167,6 @@ void DevicePolicyIdentityStrategy::OnDeviceTokenAvailable( const std::string& token) { DCHECK(!machine_id_.empty()); - // Reset registration flag, so we only attempt registration once. - should_register_ = false; - device_token_ = token; NotifyDeviceTokenChanged(); } @@ -86,8 +174,10 @@ void DevicePolicyIdentityStrategy::OnDeviceTokenAvailable( void DevicePolicyIdentityStrategy::CheckAndTriggerFetch() { std::string username; std::string auth_token; - if (GetCredentials(&username, &auth_token)) + if (GetCredentials(&username, &auth_token)) { + device_id_ = guid::GenerateGUID(); NotifyAuthChanged(); + } } void DevicePolicyIdentityStrategy::Observe(NotificationType type, @@ -97,15 +187,11 @@ void DevicePolicyIdentityStrategy::Observe(NotificationType type, const TokenService::TokenAvailableDetails* token_details = Details<const TokenService::TokenAvailableDetails>(details).ptr(); if (token_details->service() == GaiaConstants::kDeviceManagementService) - CheckAndTriggerFetch(); + CheckOwnershipAndTriggerFetch(); } else if (type == NotificationType::LOGIN_USER_CHANGED) { - should_register_ = false; - CheckAndTriggerFetch(); - } else if (type == NotificationType::OWNERSHIP_TAKEN) { - should_register_ = true; - CheckAndTriggerFetch(); + CheckOwnershipAndTriggerFetch(); } else if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) { - CheckAndTriggerFetch(); + CheckOwnershipAndTriggerFetch(); } else { NOTREACHED(); } diff --git a/chrome/browser/policy/device_policy_identity_strategy.h b/chrome/browser/policy/device_policy_identity_strategy.h index 838124d..913a2a3 100644 --- a/chrome/browser/policy/device_policy_identity_strategy.h +++ b/chrome/browser/policy/device_policy_identity_strategy.h @@ -8,6 +8,8 @@ #include <string> +#include "base/ref_counted.h" +#include "base/weak_ptr.h" #include "chrome/browser/policy/cloud_policy_identity_strategy.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -21,20 +23,32 @@ class DevicePolicyIdentityStrategy : public CloudPolicyIdentityStrategy, public NotificationObserver { public: DevicePolicyIdentityStrategy(); - virtual ~DevicePolicyIdentityStrategy() {} + virtual ~DevicePolicyIdentityStrategy(); + + // Called by DevicePolicyIdentityStrategy::OwnershipChecker: + virtual void OnOwnershipInformationAvailable(bool current_user_is_owner); // CloudPolicyIdentityStrategy implementation: virtual std::string GetDeviceToken(); virtual std::string GetDeviceID(); + virtual std::string GetMachineID(); + virtual em::DeviceRegisterRequest_Type GetPolicyRegisterType(); + virtual std::string GetPolicyType(); virtual bool GetCredentials(std::string* username, std::string* auth_token); virtual void OnDeviceTokenAvailable(const std::string& token); private: + class OwnershipChecker; + // Recheck whether all parameters are available and if so, trigger a // credentials changed notification. void CheckAndTriggerFetch(); + // Updates the ownership information and then passes control to + // |CheckAndTriggerFetch|. + void CheckOwnershipAndTriggerFetch(); + // NotificationObserver method overrides: virtual void Observe(NotificationType type, const NotificationSource& source, @@ -43,17 +57,27 @@ class DevicePolicyIdentityStrategy : public CloudPolicyIdentityStrategy, // The machine identifier. std::string machine_id_; + // The device identifier to be sent with requests. (This is actually more like + // a session identifier since it is re-generated for each registration + // request.) + std::string device_id_; + // Current token. Empty if not available. std::string device_token_; - // Whether to try and register. Device policy enrollment does not happen - // automatically except for the case that the device gets claimed. This - // situation is detected by listening for the OWNERSHIP_TAKEN notification. - bool should_register_; + // Whether the currently logged in user is the device's owner. This variable + // is owned by the UI thread but updated from the FILE thread. Therefore + // after an owner login it will take some time before it turns to true. + bool current_user_is_owner_; // Registers the provider for notification of successful Gaia logins. NotificationRegistrar registrar_; + scoped_refptr<OwnershipChecker> ownership_checker_; + + // Allows to construct weak ptrs. + base::WeakPtrFactory<DevicePolicyIdentityStrategy> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(DevicePolicyIdentityStrategy); }; diff --git a/chrome/browser/policy/device_token_fetcher.cc b/chrome/browser/policy/device_token_fetcher.cc index 6661d3c..a426cf4 100644 --- a/chrome/browser/policy/device_token_fetcher.cc +++ b/chrome/browser/policy/device_token_fetcher.cc @@ -9,6 +9,7 @@ #include "base/message_loop.h" #include "chrome/browser/policy/cloud_policy_cache.h" #include "chrome/browser/policy/device_management_service.h" +#include "chrome/browser/policy/proto/device_management_constants.h" #include "chrome/browser/policy/proto/device_management_local.pb.h" namespace { @@ -50,11 +51,16 @@ DeviceTokenFetcher::~DeviceTokenFetcher() { CancelRetryTask(); } -void DeviceTokenFetcher::FetchToken(const std::string& auth_token, - const std::string& device_id) { +void DeviceTokenFetcher::FetchToken( + const std::string& auth_token, + const std::string& device_id, + em::DeviceRegisterRequest_Type policy_type, + const std::string& machine_id) { SetState(STATE_INACTIVE); auth_token_ = auth_token; device_id_ = device_id; + policy_type_ = policy_type; + machine_id_ = machine_id; FetchTokenInternal(); } @@ -64,6 +70,10 @@ void DeviceTokenFetcher::FetchTokenInternal() { // Construct a new backend, which will discard any previous requests. backend_.reset(service_->CreateBackend()); em::DeviceRegisterRequest request; + request.set_type(policy_type_); + if (!machine_id_.empty()) + request.set_machine_id(machine_id_); + request.set_machine_model(kRegisterRequestMachineModel); backend_->ProcessRegisterRequest(auth_token_, device_id_, request, this); } diff --git a/chrome/browser/policy/device_token_fetcher.h b/chrome/browser/policy/device_token_fetcher.h index 0501433..7f78100 100644 --- a/chrome/browser/policy/device_token_fetcher.h +++ b/chrome/browser/policy/device_token_fetcher.h @@ -12,6 +12,7 @@ #include "base/scoped_ptr.h" #include "base/task.h" #include "chrome/browser/policy/device_management_backend.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" namespace policy { @@ -48,7 +49,9 @@ class DeviceTokenFetcher // Starts fetching a token. // Declared virtual so it can be overridden by mocks. virtual void FetchToken(const std::string& auth_token, - const std::string& device_id); + const std::string& device_id, + em::DeviceRegisterRequest_Type policy_type, + const std::string& machine_id); // Returns the device management token or the empty string if not available. // Declared virtual so it can be overridden by mocks. @@ -125,6 +128,10 @@ class DeviceTokenFetcher std::string auth_token_; // Device identifier to send to the server. std::string device_id_; + // Contains policy type to send to the server. + em::DeviceRegisterRequest_Type policy_type_; + // Contains physical machine id to send to the server. + std::string machine_id_; // Task that has been scheduled to retry fetching a token. CancelableTask* retry_task_; diff --git a/chrome/browser/policy/device_token_fetcher_unittest.cc b/chrome/browser/policy/device_token_fetcher_unittest.cc index d040fb4..95e4dda 100644 --- a/chrome/browser/policy/device_token_fetcher_unittest.cc +++ b/chrome/browser/policy/device_token_fetcher_unittest.cc @@ -10,6 +10,7 @@ #include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/policy/cloud_policy_cache.h" #include "chrome/browser/policy/device_management_service.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" #include "chrome/browser/policy/mock_device_management_backend.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/test/testing_profile.h" @@ -85,14 +86,6 @@ class ProxyDeviceManagementBackend : public DeviceManagementBackend { backend_->ProcessPolicyRequest(device_management_token, device_id, request, delegate); } - virtual void ProcessCloudPolicyRequest( - const std::string& device_management_token, - const std::string& device_id, - const em::CloudPolicyRequest& request, - DevicePolicyResponseDelegate* delegate) { - backend_->ProcessCloudPolicyRequest(device_management_token, device_id, - request, delegate); - } private: DeviceManagementBackend* backend_; // weak @@ -142,7 +135,8 @@ TEST_F(DeviceTokenFetcherTest, FetchToken) { EXPECT_CALL(observer, OnDeviceTokenAvailable()); fetcher.AddObserver(&observer); EXPECT_EQ("", fetcher.GetDeviceToken()); - fetcher.FetchToken("fake_auth_token", "fake_device_id"); + fetcher.FetchToken("fake_auth_token", "fake_device_id", + em::DeviceRegisterRequest::USER, "fake_machine_id"); loop_.RunAllPending(); Mock::VerifyAndClearExpectations(&observer); std::string token = fetcher.GetDeviceToken(); @@ -152,7 +146,8 @@ TEST_F(DeviceTokenFetcherTest, FetchToken) { EXPECT_CALL(backend_, ProcessRegisterRequest(_, _, _, _)).WillOnce( MockDeviceManagementBackendSucceedRegister()); EXPECT_CALL(observer, OnDeviceTokenAvailable()); - fetcher.FetchToken("fake_auth_token", "fake_device_id"); + fetcher.FetchToken("fake_auth_token", "fake_device_id", + em::DeviceRegisterRequest::USER, "fake_machine_id"); loop_.RunAllPending(); Mock::VerifyAndClearExpectations(&observer); std::string token2 = fetcher.GetDeviceToken(); @@ -171,7 +166,8 @@ TEST_F(DeviceTokenFetcherTest, RetryOnError) { MockTokenAvailableObserver observer; EXPECT_CALL(observer, OnDeviceTokenAvailable()); fetcher.AddObserver(&observer); - fetcher.FetchToken("fake_auth_token", "fake_device_id"); + fetcher.FetchToken("fake_auth_token", "fake_device_id", + em::DeviceRegisterRequest::USER, "fake_machine_id"); loop_.RunAllPending(); Mock::VerifyAndClearExpectations(&observer); EXPECT_NE("", fetcher.GetDeviceToken()); @@ -187,7 +183,8 @@ TEST_F(DeviceTokenFetcherTest, UnmanagedDevice) { MockTokenAvailableObserver observer; EXPECT_CALL(observer, OnDeviceTokenAvailable()).Times(0); fetcher.AddObserver(&observer); - fetcher.FetchToken("fake_auth_token", "fake_device_id"); + fetcher.FetchToken("fake_auth_token", "fake_device_id", + em::DeviceRegisterRequest::USER, "fake_machine_id"); loop_.RunAllPending(); Mock::VerifyAndClearExpectations(&observer); EXPECT_EQ("", fetcher.GetDeviceToken()); diff --git a/chrome/browser/policy/mock_device_management_backend.h b/chrome/browser/policy/mock_device_management_backend.h index 54c8c8f..d360157 100644 --- a/chrome/browser/policy/mock_device_management_backend.h +++ b/chrome/browser/policy/mock_device_management_backend.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -12,6 +12,8 @@ #include "base/time.h" #include "base/values.h" #include "chrome/browser/policy/device_management_backend.h" +#include "chrome/browser/policy/proto/cloud_policy.pb.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" #include "chrome/browser/policy/proto/device_management_constants.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -47,12 +49,6 @@ class MockDeviceManagementBackend : public DeviceManagementBackend { const em::DevicePolicyRequest& request, DevicePolicyResponseDelegate* delegate)); - MOCK_METHOD4(ProcessCloudPolicyRequest, void( - const std::string& device_management_token, - const std::string& device_id, - const em::CloudPolicyRequest& request, - DevicePolicyResponseDelegate* delegate)); - private: DISALLOW_COPY_AND_ASSIGN(MockDeviceManagementBackend); }; @@ -81,21 +77,26 @@ ACTION_P2(MockDeviceManagementBackendSucceedBooleanPolicy, name, value) { } ACTION(MockDeviceManagementBackendSucceedSpdyCloudPolicy) { - em::SignedCloudPolicyResponse signed_response; - em::CloudPolicySettings* settings = signed_response.mutable_settings(); - em::DisableSpdyProto* spdy_proto = settings->mutable_disablespdy(); + em::PolicyData signed_response; + em::CloudPolicySettings settings; + em::DisableSpdyProto* spdy_proto = settings.mutable_disablespdy(); spdy_proto->set_disablespdy(true); spdy_proto->mutable_policy_options()->set_mode(em::PolicyOptions::MANDATORY); - signed_response.set_timestamp(base::Time::NowFromSystemTime().ToTimeT()); + EXPECT_TRUE( + settings.SerializeToString(signed_response.mutable_policy_value())); + base::TimeDelta timestamp = + base::Time::NowFromSystemTime() - base::Time::UnixEpoch(); + signed_response.set_timestamp(timestamp.InMilliseconds()); std::string serialized_signed_response; EXPECT_TRUE(signed_response.SerializeToString(&serialized_signed_response)); - em::CloudPolicyResponse response; - response.set_signed_response(serialized_signed_response); + em::DevicePolicyResponse response; + em::PolicyFetchResponse* fetch_response = response.add_response(); + fetch_response->set_policy_data(serialized_signed_response); // TODO(jkummerow): Set proper certificate_chain and signature (when // implementing support for signature verification). - response.set_signature("TODO"); - response.add_certificate_chain("TODO"); - arg3->HandleCloudPolicyResponse(response); + fetch_response->set_policy_data_signature("TODO"); + fetch_response->add_certificate_chain("TODO"); + arg3->HandlePolicyResponse(response); } ACTION_P(MockDeviceManagementBackendFailRegister, error) { diff --git a/chrome/browser/policy/proto/device_management_backend.proto b/chrome/browser/policy/proto/device_management_backend.proto index 1a857b6..a192a25 100644 --- a/chrome/browser/policy/proto/device_management_backend.proto +++ b/chrome/browser/policy/proto/device_management_backend.proto @@ -4,18 +4,31 @@ syntax = "proto2"; -import "cloud_policy.proto"; - option optimize_for = LITE_RUNTIME; package enterprise_management; -// Protocol buffers for the obsolete protocol: -// ------------------------------------------- -// GenericValue, GenericNamedValue, GenericSetting, DevicePolicySetting, -// DevicePolicySettingRequest, DevicePolicyRequest, DevicePolicyResponse -// TODO(gfeher): Remove these when both Chrome and DMServer is switched to -// using the new protocol. +// Meta-settings that control how a user receives regular settings +// (CloudPolicySettings) for Chrome. The name "Initial" indicates that +// these settings will be downloaded before Chrome starts requesting +// regular settings. +message ChromeInitialSettingsProto { + enum EnrollmentProvision { + // The users's device is not automatically enrolled for policies, but the + // user may choose to try to enroll it. + UNMANAGED = 0; + // The user must enroll its device for policies. + MANAGED = 1; + } + // Chrome will interpret this as UNMANAGED if unset. + optional EnrollmentProvision enrollment_provision = 1 [default = UNMANAGED]; +} + +// A setting is a set of generic name value pairs. +// TODO(gfeher): remove this after Chrome OS TT is over. +message GenericSetting { + repeated GenericNamedValue named_value = 1; +} // Generic value container. message GenericValue { @@ -51,12 +64,8 @@ message GenericNamedValue { optional GenericValue value = 2; } -// A setting is a set of generic name value pairs. -message GenericSetting { - repeated GenericNamedValue named_value = 1; -} - // Identify a single device policy setting key/value pair. +// TODO(gfeher): remove this after Chrome OS TT is over. message DevicePolicySetting { // key of the policy setting required string policy_key = 1; @@ -66,189 +75,205 @@ message DevicePolicySetting { optional string watermark = 3; } -// Request for a setting or with optional watermark on client side. -message DevicePolicySettingRequest { - // setting key - required string key = 1; - // watermark last read from server if available. - optional string watermark = 2; -} - -// Request from device to server to read device policies. -message DevicePolicyRequest { - // identify request scope: CrOS settings or other type of settings. - optional string policy_scope = 1; - // identify key to the settings: proxy etc. - repeated DevicePolicySettingRequest setting_request = 2; -} - -// Response from server to agent for reading policies. -message DevicePolicyResponse { - // the result of the settings. - repeated DevicePolicySetting setting = 1; -} - -// Request from device to server to register device. The response will include -// a device token that can be used to query policies. +// Request from device to server to register device. message DeviceRegisterRequest { - // reregister device without erasing server state. - // it can be used to refresh dmtoken etc. + // Reregister device without erasing server state. It can be used + // to refresh dmtoken etc. Client MUST set this value to true if it + // reuses an existing device id. optional bool reregister = 1; + + // Device register type. This field does not exist for TT release. + // When a client requests for policies, server should verify the + // client has been registered properly. For example, a client must + // register with type DEVICE in order to retrieve device policies. + enum Type { + TT = 0; // Register for TT release. + USER = 1; // Register for user polices. + DEVICE = 2; // Register for device policies. + } + // NOTE: we also use this field to detect client version. If this + // field is missing, then the request comes from TT. We will remove + // Chrome OS TT support once it is over. + optional Type type = 2 [default = TT]; + + // Machine hardware id, such as MEID, Mac adress. + // This field is required if register type == DEVICE. + optional string machine_id = 3; + + // Machine model name, such as "ZGA", "Cr-48", "Nexus One". If the + // model name is not available, client SHOULD send generic name like + // "Android", or "Chrome OS". + optional string machine_model = 4; } // Response from server to device register request. message DeviceRegisterResponse { - // device mangement toke for this registration. + // Device mangement token for this registration. This token MUST be + // part of HTTP Authorization header for all future requests from + // device to server. required string device_management_token = 1; -} -// Protocol buffers for the new protocol: -// -------------------------------------- - -// Request from device to server to get policies for an unregistered user. -// These are actually "meta-policies", that control the rules for the user -// about enrolling for real policies. -message InitialPolicyRequest { + // Device display name. By default, server generates the name in + // the format of "Machine Model - Machine Id". However, domain + // admin can update it using CPanel, so do NOT treat it as constant. + optional string machine_name = 2; } -message InitialPolicySettings { - enum EnrollmentRule { - // The user must enroll its device for policies. - MANAGED = 1; - // The users's device is not automatically enrolled for policies, but the - // user may choose to try to enroll it. - UNMANAGED = 2; - } - - optional EnrollmentRule enrollment_rule = 1; +// Request from device to server to unregister device. +// GoogleDMToken MUST be in HTTP Authorization header. +message DeviceUnregisterRequest { } -// Response from server to device containing the policies available before -// registration. -message InitialPolicyResponse { - optional InitialPolicySettings settings = 1; +// Response from server to device for unregister request. +message DeviceUnregisterResponse { } -// Request from device to server to unregister device management token. -message DeviceUnregisterRequest { +// Request for a setting or with optional watermark on client side. +// TODO(gfeher): remove this after Chrome OS TT is over. +message DevicePolicySettingRequest { + // setting key + required string key = 1; + // watermark last read from server if available. + optional string watermark = 2; } -// Response from server to unregister request. -message DeviceUnregisterResponse { +message PolicyFetchRequest { + // This is the policy type, which maps to D3 policy type internally. + // By convention, we use "/" as separator to create policy namespace. + // The policy type names are case insensitive. + // + // Possible values for Chrome OS are: + // google/chromeos/device => ChromeSettingsProto + // google/chromeos/user => ChromeSettingsProto + // google/chromeos/unregistered_user => ChromeInitialSettingsProto + optional string policy_type = 1; + + // This is the last policy timestamp that client received from server. + optional int64 timestamp = 2; + + // Tell server what kind of security signature is required. + enum SignatureType { + NONE = 0; + X509 = 1; + } + optional SignatureType signature_type = 3 [default = NONE]; } -// Request from device to server to register device. The response will include -// a device token that can be used to query policies. -message CloudRegisterRequest { - enum Type { - // Requesting token for user policies. - USER = 1; - // Requesting token for device policies. - DEVICE = 2; - } - optional Type type = 1; - // Unique identifier of the machine. Only set if type == DEVICE. - // This won't be sent in later requests, the machine can be identified - // by its device token. - optional string machine_id = 2; +// This message is included in serialized form in PolicyFetchResponse +// below. It may also be signed, with the signature being created for +// the serialized form. +message PolicyData { + // See PolicyFetchRequest.policy_type. + optional string policy_type = 1; + + // [timestamp] is milli seconds since Epoch in UTC timezone. It is + // included here so that the time at which the server issued this + // response cannot be faked (as protection against replay attacks). + // It is the timestamp generated by DMServer, NOT the time admin + // last updated the policy or anything like that. + optional int64 timestamp = 2; + + // The DM token that was used by the client in the HTTP POST header + // for authenticating the request. It is included here again so that + // the client can verify that the response is meant for him (and not + // issued by a replay or man-in-the-middle attack). + optional string request_token = 3; + + // The serialized value of the actual policy protobuf. This can be + // deserialized to an instance of, for example, ChromeSettingsProto + // or ChromeUserSettingsProto. + optional bytes policy_value = 4; + + // The device display name assigned by the server. It is only + // filled if the display name is available. + // + // The display name of the machine as generated by the server or set + // by the Administrator in the CPanel GUI. This is the same thing as + // |machine_name| in DeviceRegisterResponse but it might have + // changed since then. + optional string machine_name = 5; } -// Response from server to device register request. -message CloudRegisterResponse { - // Token for this registration. - required string device_management_token = 1; +message PolicyFetchResponse { + // Since a single policy request may ask for multiple policies, we + // provide separate error code for each individual policy fetch. - // The name of the requesting device, assigned by the server. - optional string machine_name = 2; + // We will use standard HTTP Status Code as error code. + optional int32 error_code = 1; + + // Human readable error message for customer support purpose. + optional string error_message = 2; + + // This is a serialized bytes of PolicyData protobuf above. + optional bytes policy_data = 3; + + // Signature of the policy data above. + optional bytes policy_data_signature = 4; + + // The chain of DER-encoded X.509 certificates of the server's + // signing key. The first element should be the certificate whose + // private key was used for signing the response, and each of the + // following certificates signs the previous one. + // + // If this field does not exist, it means the policy_data is not + // signed. + repeated bytes certificate_chain = 5; } -message CloudPolicyRequest { - // Identify request scope: chromeos/device for device policies, chromeos/user - // for user policies. Only those policy scopes will be served, that are - // allowed by the type choice in CloudRegisterRequest. +// Request from device to server for reading policies. +message DevicePolicyRequest { + // identify request scope: CrOS settings or other type of settings. + // TODO(gfeher): remove this after Chrome OS TT is over. optional string policy_scope = 1; + // identify key to the settings: proxy etc. + // TODO(gfeher): remove this after Chrome OS TT is over. + repeated DevicePolicySettingRequest setting_request = 2; - // The token used to query device policies on the device sending the request. - // Note, that the token used for actual authentication is sent in an HTTP - // header. These two tokens are the same if this request is for querying - // device policies and they differ if this request is for querying user - // policies. In the second case, the server can use device_policy_token to - // identify the device and determine if the user is allowed to get policies - // on the given device. - optional string device_policy_token = 2; + // The policy fetch request. If this field exists, the request must + // comes from a non-TT client. The repeated field allows client to + // request multiple policies for better performance. + repeated PolicyFetchRequest request = 3; } // Response from server to device for reading policies. -message CloudPolicyResponse { - // Serialized SignedCloudPolicyResponse. - optional bytes signed_response = 1; - // RSA signature of the SHA1 hash of the above data. - optional bytes signature = 2; - // The chain of DER-encoded X.509 certificates of the server's signing key. - // The first element should be the certificate whose private key was used - // for signing the response, and each of the following certificates signs the - // previous one. - repeated bytes certificate_chain = 3; -} -message SignedCloudPolicyResponse { - // The following two are necessary against replay attacks. - // |timestamp| is a unix timestamp (seconds since 1970). - optional int64 timestamp = 1; - // The token that was used for the request. - optional string request_token = 2; - // The name of the device, assigned by the server. - optional string device_name = 3; - // CloudPolicySettings is defined in cloud_policy.proto (which is - // auto-generated from chrome/app/policy_templates.json). - optional CloudPolicySettings settings = 4; +message DevicePolicyResponse { + // the result of the settings. + // TODO(gfeher): remove this after Chrome OS TT is over. + repeated DevicePolicySetting setting = 1; + + // The policy fetch response. + repeated PolicyFetchResponse response = 3; } -// Request from the DMAgent on the device to the DMServer. -// This is container for all requests from client. +// Request from the DMAgent on the device to the DMServer. This is +// container for all requests from device to server. The overall HTTP +// request MUST be in the following format: // -// Http Query parameters: -// Query parameters contain the following information in each request: -// request: register/unregister/policy/cloud_policy/cloud_register/ -// initial_policy -// devicetype: CrOS/Android/Iphone etc. -// apptype: CrOS/AndroidDM etc. -// agent: identify agent on device. +// * HTTP method is POST +// * Data mime type is application/x-protobuffer +// * HTTP parameters are (all required, all case sensitive): +// * request: MUST BE one of register/unregister/policy/ping +// * devicetype: MUST BE "1" for Android or "2" for Chrome OS. +// * apptype: MUST BE Android or Chrome. +// * deviceid: MUST BE no more than 64-char in [\x20-\x7E]. +// * agent: MUST BE no more than 64-char long. +// * HTTP Authorization header MUST be in the following formats: +// * For register and ping requests +// Authorization: GoogleLogin auth=<auth cookie for Mobile Sync> // -// Authorization: -// 1. If request is initial_policy, client must pass in GoogleLogin -// auth cookie in Authorization header: -// Authorization: GoogleLogin auth=<auth cookie> -// The response will contain settings that a user can get without -// registration. Currently the only such setting is a flag indicating if the -// user is in a managed domain or not. (We don't want to expose device ids of -// users not in managed domains.) -// 2. If request is register_request, client must pass in GoogleLogin auth -// cookie in Authorization header: -// Authorization: GoogleLogin auth=<auth cookie> -// The response will contain an unique DMToken for future requests. -// Depending on domain policy, the request may need admin approval before -// DMToken is issued. -// 3. For other requests, client must pass in DMToken in Authorization header: -// Authorization: GoogleDMToken token=<google dm token> +// * For unregister and policy requests +// Authorization: GoogleDMToken token=<dm token from register> // +// * OAuth is NOT supported yet. message DeviceManagementRequest { - // Register request (old protocol). + // Register request. optional DeviceRegisterRequest register_request = 1; // Unregister request. optional DeviceUnregisterRequest unregister_request = 2; - // Data request. + // Policy request. optional DevicePolicyRequest policy_request = 3; - - // Data request (new protocol). - optional CloudPolicyRequest cloud_policy_request = 4; - - // Request for initial (before registration) policies. - optional InitialPolicyRequest initial_policy_request = 5; - - // Register request (new protocol). - optional CloudRegisterRequest cloud_register_request = 6; } // Response from server to device. @@ -276,7 +301,7 @@ message DeviceManagementResponse { // Error message. optional string error_message = 2; - // Register response (old protocol). + // Register response optional DeviceRegisterResponse register_response = 3; // Unregister response @@ -284,13 +309,4 @@ message DeviceManagementResponse { // Policy response. optional DevicePolicyResponse policy_response = 5; - - // Policy response (new protocol). - optional CloudPolicyResponse cloud_policy_response = 6; - - // Response to initial (before registration) policy request. - optional InitialPolicyResponse initial_policy_response = 7; - - // Register response (new protocol). - optional CloudRegisterResponse cloud_register_response = 8; }
\ No newline at end of file diff --git a/chrome/browser/policy/proto/device_management_constants.cc b/chrome/browser/policy/proto/device_management_constants.cc index 3912eb1..bb57c8d 100644 --- a/chrome/browser/policy/proto/device_management_constants.cc +++ b/chrome/browser/policy/proto/device_management_constants.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -9,4 +9,9 @@ namespace policy { const char kChromePolicyScope[] = "chromeos/device"; const char kChromeDevicePolicySettingKey[] = "chrome-policy"; +const char kChromeDevicePolicyType[] = "google/chromeos/device"; +const char kChromeUserPolicyType[] = "google/chromeos/user"; + +const char kRegisterRequestMachineModel[] = "Chrome OS"; + } // namespace policy diff --git a/chrome/browser/policy/proto/device_management_constants.h b/chrome/browser/policy/proto/device_management_constants.h index e37c514..920ff91 100644 --- a/chrome/browser/policy/proto/device_management_constants.h +++ b/chrome/browser/policy/proto/device_management_constants.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -10,6 +10,11 @@ namespace policy { extern const char kChromePolicyScope[]; extern const char kChromeDevicePolicySettingKey[]; +extern const char kChromeDevicePolicyType[]; +extern const char kChromeUserPolicyType[]; + +extern const char kRegisterRequestMachineModel[]; + } // namespace policy #endif // CHROME_BROWSER_POLICY_PROTO_DEVICE_MANAGEMENT_CONSTANTS_H_ diff --git a/chrome/browser/policy/proto/device_management_local.proto b/chrome/browser/policy/proto/device_management_local.proto index 45c2994..122e863 100644 --- a/chrome/browser/policy/proto/device_management_local.proto +++ b/chrome/browser/policy/proto/device_management_local.proto @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -14,14 +14,14 @@ import "device_management_backend.proto"; message CachedCloudPolicyResponse { // The DevicePolicyResponse wrapped by this message. optional DevicePolicyResponse device_policy = 1; - // Timestamp noting when the |unmanaged| flag was set. The data format is - // a unix timestamp. When caching (deprecated) DevicePolicyResponses, this - // timestamp also notes when the response was cached. + // Timestamp noting when the |unmanaged| flag was set. The meaning is + // milliseconds since 1970. When caching (deprecated) DevicePolicyResponses, + // this timestamp also notes when the response was cached. optional uint64 timestamp = 2; // Flag that is set to true if this device is not managed. optional bool unmanaged = 3; - // The CloudPolicyResponse wrapped by this message. - optional CloudPolicyResponse cloud_policy = 4; + // The PolicyFetchResponse wrapped by this message. + optional PolicyFetchResponse cloud_policy = 4; } // Encapsulates a device ID and the associated device token. @@ -29,4 +29,3 @@ message DeviceCredentials { optional string device_id = 1; optional string device_token = 2; } - diff --git a/chrome/browser/policy/user_policy_identity_strategy.cc b/chrome/browser/policy/user_policy_identity_strategy.cc index a49f311..e900390 100644 --- a/chrome/browser/policy/user_policy_identity_strategy.cc +++ b/chrome/browser/policy/user_policy_identity_strategy.cc @@ -7,6 +7,8 @@ #include "base/file_util.h" #include "chrome/browser/browser_signin.h" #include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" +#include "chrome/browser/policy/proto/device_management_constants.h" #include "chrome/browser/policy/proto/device_management_local.pb.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/guid.h" @@ -163,6 +165,20 @@ std::string UserPolicyIdentityStrategy::GetDeviceID() { return device_id_; } +std::string UserPolicyIdentityStrategy::GetMachineID() { + return ""; +} + +em::DeviceRegisterRequest_Type +UserPolicyIdentityStrategy::GetPolicyRegisterType() { + return em::DeviceRegisterRequest::USER; +} + +std::string UserPolicyIdentityStrategy::GetPolicyType() { + return kChromeUserPolicyType; +} + + bool UserPolicyIdentityStrategy::GetCredentials(std::string* username, std::string* auth_token) { *username = GetCurrentUser(); @@ -221,7 +237,11 @@ void UserPolicyIdentityStrategy::Observe(NotificationType type, const TokenService::TokenAvailableDetails* token_details = Details<const TokenService::TokenAvailableDetails>(details).ptr(); if (token_details->service() == GaiaConstants::kDeviceManagementService) - CheckAndTriggerFetch(); + if (device_token_.empty()) { + // Request a new device management server token, but only in case we + // don't already have it. + CheckAndTriggerFetch(); + } } #if defined(OS_CHROMEOS) } else if (type == NotificationType::LOGIN_USER_CHANGED) { diff --git a/chrome/browser/policy/user_policy_identity_strategy.h b/chrome/browser/policy/user_policy_identity_strategy.h index bf6ca55..2ba3e43 100644 --- a/chrome/browser/policy/user_policy_identity_strategy.h +++ b/chrome/browser/policy/user_policy_identity_strategy.h @@ -33,6 +33,10 @@ class UserPolicyIdentityStrategy : public CloudPolicyIdentityStrategy, // CloudPolicyIdentityStrategy implementation: virtual std::string GetDeviceToken(); virtual std::string GetDeviceID(); + virtual std::string GetMachineID(); + virtual em::DeviceRegisterRequest_Type GetPolicyRegisterType(); + virtual std::string GetPolicyType(); + virtual bool GetCredentials(std::string* username, std::string* auth_token); virtual void OnDeviceTokenAvailable(const std::string& token); diff --git a/chrome/test/data/policy/device_management b/chrome/test/data/policy/device_management index 4cd0371..a64ce58 100644 --- a/chrome/test/data/policy/device_management +++ b/chrome/test/data/policy/device_management @@ -1,5 +1,5 @@ { - "chromeos/device": { + "google/chromeos/user": { "mandatory": { "HomepageLocation" : "http://www.chromium.org" }, diff --git a/chrome/tools/build/generate_policy_source.py b/chrome/tools/build/generate_policy_source.py index f573484..4de4fef 100644 --- a/chrome/tools/build/generate_policy_source.py +++ b/chrome/tools/build/generate_policy_source.py @@ -226,7 +226,7 @@ CPP_HEAD = ''' #include "base/values.h" #include "chrome/browser/policy/configuration_policy_provider.h" #include "chrome/browser/policy/policy_map.h" -#include "chrome/browser/policy/proto/device_management_backend.pb.h" +#include "chrome/browser/policy/proto/cloud_policy.pb.h" #include "policy/configuration_policy_type.h" using google::protobuf::RepeatedPtrField; diff --git a/net/tools/testserver/device_management.py b/net/tools/testserver/device_management.py index ccf1ac8..efd6694 100644 --- a/net/tools/testserver/device_management.py +++ b/net/tools/testserver/device_management.py @@ -114,19 +114,25 @@ class RequestHandler(object): rmsg = dm.DeviceManagementRequest() rmsg.ParseFromString(self._request) + logging.debug('auth -> ' + self._headers.getheader('Authorization', '')) + logging.debug('deviceid -> ' + self.GetUniqueParam('deviceid')) self.DumpMessage('Request', rmsg) request_type = self.GetUniqueParam('request') + # Check server side requirements, as defined in + # device_management_backend.proto. + if (self.GetUniqueParam('devicetype') != '2' or + self.GetUniqueParam('apptype') != 'Chrome' or + (request_type != 'ping' and + len(self.GetUniqueParam('deviceid')) >= 64) or + len(self.GetUniqueParam('agent')) >= 64): + return (400, 'Invalid request parameter') if request_type == 'register': return self.ProcessRegister(rmsg.register_request) elif request_type == 'unregister': return self.ProcessUnregister(rmsg.unregister_request) - elif request_type == 'policy': - return self.ProcessPolicy(rmsg.policy_request) - elif request_type == 'cloud_policy': - return self.ProcessCloudPolicyRequest(rmsg.cloud_policy_request) - elif request_type == 'managed_check': - return self.ProcessManagedCheck(rmsg.managed_check_request) + elif request_type == 'policy' or request_type == 'ping': + return self.ProcessPolicy(rmsg.policy_request, request_type) else: return (400, 'Invalid request parameter') @@ -140,12 +146,6 @@ class RequestHandler(object): return None return match.group(1) - def GetDeviceName(self): - """Returns the name for the currently authenticated device based on its - device id. - """ - return 'chromeos-' + self.GetUniqueParam('deviceid') - def ProcessRegister(self, msg): """Handles a register request. @@ -166,13 +166,16 @@ class RequestHandler(object): if not device_id: return (400, 'Missing device identifier') - # Register the device and create a token. - dmtoken = self._server.RegisterDevice(device_id) + token_info = self._server.RegisterDevice(device_id, + msg.machine_id, + msg.type) # Send back the reply. response = dm.DeviceManagementResponse() response.error = dm.DeviceManagementResponse.SUCCESS - response.register_response.device_management_token = dmtoken + response.register_response.device_management_token = ( + token_info['device_token']) + response.register_response.machine_name = token_info['machine_name'] self.DumpMessage('Response', response) @@ -207,41 +210,50 @@ class RequestHandler(object): return (200, response.SerializeToString()) - def ProcessManagedCheck(self, msg): - """Handles a 'managed check' request. + def ProcessInitialPolicy(self, msg): + """Handles a 'preregister policy' request. Queries the list of managed users and responds the client if their user is managed or not. Args: - msg: The ManagedCheckRequest message received from the client. + msg: The PolicyFetchRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ - # Check the management token. + # Check the GAIA token. auth = self.CheckGoogleLogin() if not auth: return (403, 'No authorization') - managed_check_response = dm.ManagedCheckResponse() + chrome_initial_settings = dm.ChromeInitialSettingsProto() if ('*' in self._server.policy['managed_users'] or auth in self._server.policy['managed_users']): - managed_check_response.mode = dm.ManagedCheckResponse.MANAGED; + chrome_initial_settings.enrollment_provision = ( + dm.ChromeInitialSettingsProto.MANAGED); else: - managed_check_response.mode = dm.ManagedCheckResponse.UNMANAGED; + chrome_initial_settings.enrollment_provision = ( + dm.ChromeInitialSettingsProto.UNMANAGED); + + policy_data = dm.PolicyData() + policy_data.policy_type = msg.policy_type + policy_data.policy_value = chrome_initial_settings.SerializeToString() # Prepare and send the response. response = dm.DeviceManagementResponse() response.error = dm.DeviceManagementResponse.SUCCESS - response.managed_check_response.CopyFrom(managed_check_response) + fetch_response = response.policy_response.response.add() + fetch_response.policy_data = ( + policy_data.SerializeToString()) self.DumpMessage('Response', response) return (200, response.SerializeToString()) - def ProcessPolicy(self, msg): - """Handles a policy request. + def ProcessDevicePolicy(self, msg): + """Handles a policy request that uses the deprecated protcol. + TODO(gfeher): Remove this when we certainly don't need it. Checks for authorization, encodes the policy into protobuf representation and constructs the response. @@ -252,6 +264,7 @@ class RequestHandler(object): Returns: A tuple of HTTP status code and response data to send to the client. """ + # Check the management token. token, response = self.CheckToken() if not token: @@ -264,8 +277,8 @@ class RequestHandler(object): # Respond only if the client requested policy for the cros/device scope, # since that's where chrome policy is supposed to live in. - if msg.policy_scope in self._server.policy: - policy = self._server.policy[msg.policy_scope]['mandatory'] + if msg.policy_scope == 'chromeos/device': + policy = self._server.policy['google/chromeos/user']['mandatory'] setting = response.policy_response.setting.add() setting.policy_key = 'chrome-policy' policy_value = dm.GenericSetting() @@ -293,6 +306,35 @@ class RequestHandler(object): return (200, response.SerializeToString()) + def ProcessPolicy(self, msg, request_type): + """Handles a policy request. + + Checks for authorization, encodes the policy into protobuf representation + and constructs the response. + + Args: + msg: The DevicePolicyRequest message received from the client. + + Returns: + A tuple of HTTP status code and response data to send to the client. + """ + + if msg.request: + for request in msg.request: + if request.policy_type == 'google/chromeos/unregistered_user': + if request_type != 'ping': + return (400, 'Invalid request type') + return self.ProcessInitialPolicy(request) + elif (request.policy_type in + ('google/chromeos/user', 'google/chromeos/device')): + if request_type != 'policy': + return (400, 'Invalid request type') + return self.ProcessCloudPolicy(request) + else: + return (400, 'Invalid policy_type') + else: + return self.ProcessDevicePolicy(msg) + def SetProtobufMessageField(self, group_message, field, field_value): '''Sets a field in a protobuf message. @@ -302,23 +344,22 @@ class RequestHandler(object): group_message.DESCRIPTOR.fields. field_value: The value to set. ''' - if field.label == field.LABEL_REPEATED: + if field.type == field.TYPE_BOOL: + assert type(field_value) == bool + elif field.type == field.TYPE_STRING: + assert type(field_value) == str + elif field.type == field.TYPE_INT64: + assert type(field_value) == int + elif (field.type == field.TYPE_MESSAGE and + field.message_type.name == 'StringList'): assert type(field_value) == list - assert field.type == field.TYPE_STRING - list_field = group_message.__getattribute__(field.name) + entries = group_message.__getattribute__(field.name).entries for list_item in field_value: - list_field.append(list_item) + entries.append(list_item) + return else: - # Simple cases: - if field.type == field.TYPE_BOOL: - assert type(field_value) == bool - elif field.type == field.TYPE_STRING: - assert type(field_value) == str - elif field.type == field.TYPE_INT64: - assert type(field_value) == int - else: - raise Exception('Unknown field type %s' % field.type_name) - group_message.__setattr__(field.name, field_value) + raise Exception('Unknown field type %s' % field.type) + group_message.__setattr__(field.name, field_value) def GatherPolicySettings(self, settings, policies): '''Copies all the policies from a dictionary into a protobuf of type @@ -352,7 +393,7 @@ class RequestHandler(object): if got_fields: settings.__getattribute__(group.name).CopyFrom(group_message) - def ProcessCloudPolicyRequest(self, msg): + def ProcessCloudPolicy(self, msg): """Handles a cloud policy request. (New protocol for policy requests.) Checks for authorization, encodes the policy into protobuf representation, @@ -364,37 +405,37 @@ class RequestHandler(object): Returns: A tuple of HTTP status code and response data to send to the client. """ - token, response = self.CheckToken() - if not token: - return response + + token_info, error = self.CheckToken() + if not token_info: + return error settings = cp.CloudPolicySettings() - if msg.policy_scope in self._server.policy: - # Respond is only given if the scope is specified in the config file. + if (msg.policy_type in token_info['allowed_policy_types'] and + msg.policy_type in self._server.policy): + # Response is only given if the scope is specified in the config file. # Normally 'chromeos/device' and 'chromeos/user' should be accepted. self.GatherPolicySettings(settings, - self._server.policy[msg.policy_scope]) - - # Construct response - signed_response = dm.SignedCloudPolicyResponse() - signed_response.settings.CopyFrom(settings) - signed_response.timestamp = int(time.time()) - signed_response.request_token = token; - signed_response.device_name = self.GetDeviceName() - - cloud_response = dm.CloudPolicyResponse() - cloud_response.signed_response = signed_response.SerializeToString() - signed_data = cloud_response.signed_response - cloud_response.signature = ( - self._server.private_key.hashAndSign(signed_data).tostring()) - for certificate in self._server.cert_chain: - cloud_response.certificate_chain.append( - certificate.writeBytes().tostring()) + self._server.policy[msg.policy_type]) + + policy_data = dm.PolicyData() + policy_data.policy_value = settings.SerializeToString() + policy_data.policy_type = msg.policy_type + policy_data.timestamp = int(time.time() * 1000) + policy_data.request_token = token_info['device_token']; + policy_data.machine_name = token_info['machine_name'] + signed_data = policy_data.SerializeToString() response = dm.DeviceManagementResponse() response.error = dm.DeviceManagementResponse.SUCCESS - response.cloud_policy_response.CopyFrom(cloud_response) + fetch_response = response.policy_response.response.add() + fetch_response.policy_data = signed_data + fetch_response.policy_data_signature = ( + self._server.private_key.hashAndSign(signed_data).tostring()) + for certificate in self._server.cert_chain: + fetch_response.certificate_chain.append( + certificate.writeBytes().tostring()) self.DumpMessage('Response', response) @@ -404,12 +445,13 @@ class RequestHandler(object): """Helper for checking whether the client supplied a valid DM token. Extracts the token from the request and passed to the server in order to - look up the client. Returns a pair of token and error response. If the token - is None, the error response is a pair of status code and error message. + look up the client. Returns: - A pair of DM token and error response. If the token is None, the message - will contain the error response to send back. + A pair of token information record and error response. If the first + element is None, then the second contains an error code to send back to + the client. Otherwise the first element is the same structure that is + returned by LookupToken(). """ error = None dmtoken = None @@ -420,11 +462,14 @@ class RequestHandler(object): dmtoken = match.group(1) if not dmtoken: error = dm.DeviceManagementResponse.DEVICE_MANAGEMENT_TOKEN_INVALID - elif (not request_device_id or - not self._server.LookupDevice(dmtoken) == request_device_id): - error = dm.DeviceManagementResponse.DEVICE_NOT_FOUND else: - return (dmtoken, None) + token_info = self._server.LookupToken(dmtoken) + if (not token_info or + not request_device_id or + token_info['device_id'] != request_device_id): + error = dm.DeviceManagementResponse.DEVICE_NOT_FOUND + else: + return (token_info, None) response = dm.DeviceManagementResponse() response.error = error @@ -448,7 +493,7 @@ class TestServer(object): policy_cert_chain: List of paths to X.509 certificate files of the certificate chain used for signing responses. """ - self._registered_devices = {} + self._registered_tokens = {} self.policy = {} if json is None: print 'No JSON module, cannot parse policy information' @@ -485,8 +530,8 @@ class TestServer(object): handler = RequestHandler(self, path, headers, request) return handler.HandleRequest() - def RegisterDevice(self, device_id): - """Registers a device and generate a DM token for it. + def RegisterDevice(self, device_id, machine_id, type): + """Registers a device or user and generates a DM token for it. Args: device_id: The device identifier provided by the client. @@ -498,19 +543,30 @@ class TestServer(object): while len(dmtoken_chars) < 32: dmtoken_chars.append(random.choice('0123456789abcdef')) dmtoken = ''.join(dmtoken_chars) - self._registered_devices[dmtoken] = device_id - return dmtoken - - def LookupDevice(self, dmtoken): - """Looks up a device by DMToken. + allowed_policy_types = { + dm.DeviceRegisterRequest.USER: ['google/chromeos/user'], + dm.DeviceRegisterRequest.DEVICE: ['google/chromeos/device'], + dm.DeviceRegisterRequest.TT: ['google/chromeos/user'], + } + self._registered_tokens[dmtoken] = { + 'device_id': device_id, + 'device_token': dmtoken, + 'allowed_policy_types': allowed_policy_types[type], + 'machine_name': 'chromeos-' + machine_id, + } + return self._registered_tokens[dmtoken] + + def LookupToken(self, dmtoken): + """Looks up a device or a user by DM token. Args: dmtoken: The device management token provided by the client. Returns: - The corresponding device identifier or None if not found. + A dictionary with information about a device or user that is registered by + dmtoken, or None if the token is not found. """ - return self._registered_devices.get(dmtoken, None) + return self._registered_tokens.get(dmtoken, None) def UnregisterDevice(self, dmtoken): """Unregisters a device identified by the given DM token. @@ -518,5 +574,5 @@ class TestServer(object): Args: dmtoken: The device management token provided by the client. """ - if dmtoken in self._registered_devices: - del self._registered_devices[dmtoken] + if dmtoken in self._registered_tokens.keys(): + del self._registered_tokens[dmtoken] |