blob: aa1238ebe66f8089ef8151fb1eb8e607b2532f6a [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/policy/consumer_enrollment_handler.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_cloud_policy_initializer.h"
#include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_delegate.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/common/url_constants.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "components/signin/core/browser/signin_manager_base.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "policy/proto/device_management_backend.pb.h"
#include "third_party/WebKit/public/web/WebTextDirection.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/window_open_disposition.h"
#include "ui/message_center/notification.h"
#include "ui/message_center/notification_types.h"
#include "ui/message_center/notifier_settings.h"
#include "url/gurl.h"
namespace em = enterprise_management;
namespace {
// Desktop notification constants.
const char kEnrollmentNotificationId[] = "consumer_management.enroll";
const char kEnrollmentNotificationUrl[] = "chrome://consumer-management/enroll";
// The path to the consumer management enrollment/unenrollment confirmation
// overlay, relative to the settings page URL.
const char kConsumerManagementOverlay[] = "consumer-management-overlay";
// Returns the account ID signed in to |profile|.
const std::string& GetAccountIdFromProfile(Profile* profile) {
return SigninManagerFactory::GetForProfile(profile)->
GetAuthenticatedAccountId();
}
class DesktopNotificationDelegate : public NotificationDelegate {
public:
// |button_click_callback| is called when the button in the notification is
// clicked.
DesktopNotificationDelegate(const std::string& id,
const base::Closure& button_click_callback);
// NotificationDelegate:
virtual std::string id() const override;
virtual void Display() override;
virtual void ButtonClick(int button_index) override;
virtual void Error() override;
virtual void Close(bool by_user) override;
virtual void Click() override;
private:
virtual ~DesktopNotificationDelegate();
std::string id_;
base::Closure button_click_callback_;
DISALLOW_COPY_AND_ASSIGN(DesktopNotificationDelegate);
};
DesktopNotificationDelegate::DesktopNotificationDelegate(
const std::string& id,
const base::Closure& button_click_callback)
: id_(id), button_click_callback_(button_click_callback) {
}
DesktopNotificationDelegate::~DesktopNotificationDelegate() {
}
std::string DesktopNotificationDelegate::id() const {
return id_;
}
void DesktopNotificationDelegate::Display() {
}
void DesktopNotificationDelegate::ButtonClick(int button_index) {
button_click_callback_.Run();
}
void DesktopNotificationDelegate::Error() {
}
void DesktopNotificationDelegate::Close(bool by_user) {
}
void DesktopNotificationDelegate::Click() {
}
} // namespace
namespace policy {
ConsumerEnrollmentHandler::ConsumerEnrollmentHandler(
ConsumerManagementService* consumer_management_service,
DeviceManagementService* device_management_service)
: Consumer("consumer_enrollment_handler"),
consumer_management_service_(consumer_management_service),
device_management_service_(device_management_service),
enrolling_profile_(NULL),
weak_ptr_factory_(this) {
registrar_.Add(this,
chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
content::NotificationService::AllSources());
}
ConsumerEnrollmentHandler::~ConsumerEnrollmentHandler() {
if (enrolling_profile_) {
ProfileOAuth2TokenServiceFactory::GetForProfile(enrolling_profile_)->
RemoveObserver(this);
}
registrar_.Remove(this,
chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
content::NotificationService::AllSources());
}
void ConsumerEnrollmentHandler::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) {
NOTREACHED() << "Unexpected notification " << type;
return;
}
Profile* profile = content::Details<Profile>(details).ptr();
if (chromeos::ProfileHelper::IsOwnerProfile(profile))
OnOwnerSignin(profile);
}
void ConsumerEnrollmentHandler::OnRefreshTokenAvailable(
const std::string& account_id) {
CHECK(enrolling_profile_);
if (account_id == GetAccountIdFromProfile(enrolling_profile_)) {
ProfileOAuth2TokenServiceFactory::GetForProfile(enrolling_profile_)->
RemoveObserver(this);
OnOwnerRefreshTokenAvailable();
}
}
void ConsumerEnrollmentHandler::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) {
DCHECK_EQ(token_request_, request);
base::MessageLoop::current()->DeleteSoon(FROM_HERE, token_request_.release());
OnOwnerAccessTokenAvailable(access_token);
}
void ConsumerEnrollmentHandler::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
DCHECK_EQ(token_request_, request);
base::MessageLoop::current()->DeleteSoon(FROM_HERE, token_request_.release());
LOG(ERROR) << "Failed to get the access token: " << error.ToString();
EndEnrollment(ConsumerManagementService::ENROLLMENT_STAGE_GET_TOKEN_FAILED);
}
void ConsumerEnrollmentHandler::OnOwnerSignin(Profile* profile) {
const ConsumerManagementService::EnrollmentStage stage =
consumer_management_service_->GetEnrollmentStage();
switch (stage) {
case ConsumerManagementService::ENROLLMENT_STAGE_NONE:
// Do nothing.
return;
case ConsumerManagementService::ENROLLMENT_STAGE_OWNER_STORED:
// Continue the enrollment process after the owner signs in.
ContinueEnrollmentProcess(profile);
return;
case ConsumerManagementService::ENROLLMENT_STAGE_SUCCESS:
case ConsumerManagementService::ENROLLMENT_STAGE_CANCELED:
case ConsumerManagementService::ENROLLMENT_STAGE_BOOT_LOCKBOX_FAILED:
case ConsumerManagementService::ENROLLMENT_STAGE_DM_SERVER_FAILED:
case ConsumerManagementService::ENROLLMENT_STAGE_GET_TOKEN_FAILED:
ShowDesktopNotificationAndResetStage(stage, profile);
return;
case ConsumerManagementService::ENROLLMENT_STAGE_REQUESTED:
case ConsumerManagementService::ENROLLMENT_STAGE_LAST:
NOTREACHED() << "Unexpected enrollment stage " << stage;
return;
}
}
void ConsumerEnrollmentHandler::ContinueEnrollmentProcess(Profile* profile) {
enrolling_profile_ = profile;
// First, we need to ensure that the refresh token is available.
const std::string& account_id = GetAccountIdFromProfile(profile);
ProfileOAuth2TokenService* token_service =
ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
if (token_service->RefreshTokenIsAvailable(account_id)) {
OnOwnerRefreshTokenAvailable();
} else {
token_service->AddObserver(this);
}
}
void ConsumerEnrollmentHandler::OnOwnerRefreshTokenAvailable() {
CHECK(enrolling_profile_);
// Now we can request the OAuth access token for device management to send the
// device registration request to the device management server.
OAuth2TokenService::ScopeSet oauth_scopes;
oauth_scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
const std::string& account_id = GetAccountIdFromProfile(enrolling_profile_);
token_request_ = ProfileOAuth2TokenServiceFactory::GetForProfile(
enrolling_profile_)->StartRequest(account_id, oauth_scopes, this);
}
void ConsumerEnrollmentHandler::OnOwnerAccessTokenAvailable(
const std::string& access_token) {
// Now that we have the access token, we got everything we need to send the
// device registration request to the device management server.
BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
DeviceCloudPolicyInitializer* initializer =
connector->GetDeviceCloudPolicyInitializer();
CHECK(initializer);
policy::DeviceCloudPolicyInitializer::AllowedDeviceModes device_modes;
device_modes[policy::DEVICE_MODE_ENTERPRISE] = true;
initializer->StartEnrollment(
em::PolicyData::CONSUMER_MANAGED,
device_management_service_,
access_token,
false, // is_auto_enrollment
device_modes,
base::Bind(&ConsumerEnrollmentHandler::OnEnrollmentCompleted,
weak_ptr_factory_.GetWeakPtr()));
}
void ConsumerEnrollmentHandler::OnEnrollmentCompleted(EnrollmentStatus status) {
if (status.status() != EnrollmentStatus::STATUS_SUCCESS) {
LOG(ERROR) << "Failed to enroll the device."
<< " status=" << status.status()
<< " client_status=" << status.client_status()
<< " http_status=" << status.http_status()
<< " store_status=" << status.store_status()
<< " validation_status=" << status.validation_status();
EndEnrollment(ConsumerManagementService::ENROLLMENT_STAGE_DM_SERVER_FAILED);
return;
}
EndEnrollment(ConsumerManagementService::ENROLLMENT_STAGE_SUCCESS);
}
void ConsumerEnrollmentHandler::EndEnrollment(
ConsumerManagementService::EnrollmentStage stage) {
Profile* profile = enrolling_profile_;
enrolling_profile_ = NULL;
consumer_management_service_->SetEnrollmentStage(stage);
if (user_manager::UserManager::Get()->IsCurrentUserOwner())
ShowDesktopNotificationAndResetStage(stage, profile);
}
void ConsumerEnrollmentHandler::ShowDesktopNotificationAndResetStage(
ConsumerManagementService::EnrollmentStage stage, Profile* profile) {
base::string16 title;
base::string16 body;
base::string16 button_label;
base::Closure button_click_callback;
if (stage == ConsumerManagementService::ENROLLMENT_STAGE_SUCCESS) {
title = l10n_util::GetStringUTF16(
IDS_CONSUMER_MANAGEMENT_ENROLLMENT_NOTIFICATION_TITLE);
body = l10n_util::GetStringUTF16(
IDS_CONSUMER_MANAGEMENT_ENROLLMENT_NOTIFICATION_BODY);
button_label = l10n_util::GetStringUTF16(
IDS_CONSUMER_MANAGEMENT_NOTIFICATION_MODIFY_SETTINGS_BUTTON);
button_click_callback = base::Bind(
&ConsumerEnrollmentHandler::OpenSettingsPage,
weak_ptr_factory_.GetWeakPtr(),
profile);
} else {
title = l10n_util::GetStringUTF16(
IDS_CONSUMER_MANAGEMENT_ENROLLMENT_FAILURE_NOTIFICATION_TITLE);
body = l10n_util::GetStringUTF16(
IDS_CONSUMER_MANAGEMENT_ENROLLMENT_FAILURE_NOTIFICATION_BODY);
button_label = l10n_util::GetStringUTF16(
IDS_CONSUMER_MANAGEMENT_NOTIFICATION_TRY_AGAIN_BUTTON);
button_click_callback = base::Bind(
&ConsumerEnrollmentHandler::TryEnrollmentAgain,
weak_ptr_factory_.GetWeakPtr(),
profile);
}
message_center::RichNotificationData optional_field;
optional_field.buttons.push_back(message_center::ButtonInfo(button_label));
Notification notification(
message_center::NOTIFICATION_TYPE_SIMPLE,
GURL(kEnrollmentNotificationUrl),
title,
body,
ui::ResourceBundle::GetSharedInstance().GetImageNamed(
IDR_CONSUMER_MANAGEMENT_NOTIFICATION_ICON),
blink::WebTextDirectionDefault,
message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
kEnrollmentNotificationId),
base::string16(), // display_source
base::UTF8ToUTF16(kEnrollmentNotificationId),
optional_field,
new DesktopNotificationDelegate(kEnrollmentNotificationId,
button_click_callback));
notification.SetSystemPriority();
g_browser_process->notification_ui_manager()->Add(notification, profile);
consumer_management_service_->SetEnrollmentStage(
ConsumerManagementService::ENROLLMENT_STAGE_NONE);
}
void ConsumerEnrollmentHandler::OpenSettingsPage(Profile* profile) const {
const GURL url(chrome::kChromeUISettingsURL);
chrome::NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK);
params.disposition = NEW_FOREGROUND_TAB;
chrome::Navigate(&params);
}
void ConsumerEnrollmentHandler::TryEnrollmentAgain(Profile* profile) const {
const GURL base_url(chrome::kChromeUISettingsURL);
const GURL url = base_url.Resolve(kConsumerManagementOverlay);
chrome::NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK);
params.disposition = NEW_FOREGROUND_TAB;
chrome::Navigate(&params);
}
} // namespace policy