• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/chromeos/policy/consumer_management_service.h"
6 
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/prefs/pref_registry_simple.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/browser_process_platform_part.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
20 #include "chrome/browser/chromeos/policy/device_cloud_policy_initializer.h"
21 #include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
22 #include "chrome/browser/chromeos/profiles/profile_helper.h"
23 #include "chrome/browser/notifications/notification.h"
24 #include "chrome/browser/notifications/notification_delegate.h"
25 #include "chrome/browser/notifications/notification_ui_manager.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28 #include "chrome/browser/signin/signin_manager_factory.h"
29 #include "chrome/browser/ui/browser_navigator.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/common/url_constants.h"
32 #include "chromeos/dbus/cryptohome/rpc.pb.h"
33 #include "chromeos/dbus/cryptohome_client.h"
34 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
35 #include "components/signin/core/browser/profile_oauth2_token_service.h"
36 #include "components/signin/core/browser/signin_manager_base.h"
37 #include "components/user_manager/user_manager.h"
38 #include "content/public/browser/notification_details.h"
39 #include "content/public/browser/notification_service.h"
40 #include "content/public/browser/notification_source.h"
41 #include "google_apis/gaia/gaia_constants.h"
42 #include "google_apis/gaia/google_service_auth_error.h"
43 #include "grit/generated_resources.h"
44 #include "grit/theme_resources.h"
45 #include "policy/proto/device_management_backend.pb.h"
46 #include "third_party/WebKit/public/web/WebTextDirection.h"
47 #include "ui/base/l10n/l10n_util.h"
48 #include "ui/base/page_transition_types.h"
49 #include "ui/base/resource/resource_bundle.h"
50 #include "ui/base/window_open_disposition.h"
51 #include "ui/message_center/notification.h"
52 #include "ui/message_center/notification_types.h"
53 #include "ui/message_center/notifier_settings.h"
54 #include "url/gurl.h"
55 
56 namespace {
57 
58 // Boot atttributes ID.
59 const char kAttributeOwnerId[] = "consumer_management.owner_id";
60 
61 // Desktop notification constants.
62 const char kEnrollmentNotificationId[] = "consumer_management.enroll";
63 const char kEnrollmentNotificationUrl[] = "chrome://consumer-management/enroll";
64 
65 // The path to the consumer management enrollment/unenrollment confirmation
66 // overlay, relative to the settings page URL.
67 const char kConsumerManagementOverlay[] = "consumer-management-overlay";
68 
69   // Returns the account ID signed in to |profile|.
GetAccountIdFromProfile(Profile * profile)70 const std::string& GetAccountIdFromProfile(Profile* profile) {
71   return SigninManagerFactory::GetForProfile(profile)->
72       GetAuthenticatedAccountId();
73 }
74 
75 class DesktopNotificationDelegate : public NotificationDelegate {
76  public:
77   // |button_click_callback| is called when the button in the notification is
78   // clicked.
79   DesktopNotificationDelegate(const std::string& id,
80                               const base::Closure& button_click_callback);
81 
82   // NotificationDelegate:
83   virtual std::string id() const OVERRIDE;
84   virtual content::WebContents* GetWebContents() const OVERRIDE;
85   virtual void Display() OVERRIDE;
86   virtual void ButtonClick(int button_index) OVERRIDE;
87   virtual void Error() OVERRIDE;
88   virtual void Close(bool by_user) OVERRIDE;
89   virtual void Click() OVERRIDE;
90 
91  private:
92   virtual ~DesktopNotificationDelegate();
93 
94   std::string id_;
95   base::Closure button_click_callback_;
96 
97   DISALLOW_COPY_AND_ASSIGN(DesktopNotificationDelegate);
98 };
99 
DesktopNotificationDelegate(const std::string & id,const base::Closure & button_click_callback)100 DesktopNotificationDelegate::DesktopNotificationDelegate(
101     const std::string& id,
102     const base::Closure& button_click_callback)
103     : id_(id), button_click_callback_(button_click_callback) {
104 }
105 
~DesktopNotificationDelegate()106 DesktopNotificationDelegate::~DesktopNotificationDelegate() {
107 }
108 
id() const109 std::string DesktopNotificationDelegate::id() const {
110   return id_;
111 }
112 
GetWebContents() const113 content::WebContents* DesktopNotificationDelegate::GetWebContents() const {
114   return NULL;
115 }
116 
Display()117 void DesktopNotificationDelegate::Display() {
118 }
119 
ButtonClick(int button_index)120 void DesktopNotificationDelegate::ButtonClick(int button_index) {
121   button_click_callback_.Run();
122 }
123 
Error()124 void DesktopNotificationDelegate::Error() {
125 }
126 
Close(bool by_user)127 void DesktopNotificationDelegate::Close(bool by_user) {
128 }
129 
Click()130 void DesktopNotificationDelegate::Click() {
131 }
132 
133 // The string of Status enum.
134 const char* kStatusString[] = {
135   "StatusUnknown",
136   "StatusEnrolled",
137   "StatusEnrolling",
138   "StatusUnenrolled",
139   "StatusUnenrolling",
140 };
141 
142 COMPILE_ASSERT(
143     arraysize(kStatusString) == policy::ConsumerManagementService::STATUS_LAST,
144     "invalid kStatusString array size.");
145 
146 }  // namespace
147 
148 namespace em = enterprise_management;
149 
150 namespace policy {
151 
ConsumerManagementService(chromeos::CryptohomeClient * client,chromeos::DeviceSettingsService * device_settings_service)152 ConsumerManagementService::ConsumerManagementService(
153     chromeos::CryptohomeClient* client,
154     chromeos::DeviceSettingsService* device_settings_service)
155     : Consumer("consumer_management_service"),
156       client_(client),
157       device_settings_service_(device_settings_service),
158       enrolling_profile_(NULL),
159       weak_ptr_factory_(this) {
160   registrar_.Add(this,
161                  chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
162                  content::NotificationService::AllSources());
163   // A NULL value may be passed in tests.
164   if (device_settings_service_)
165     device_settings_service_->AddObserver(this);
166 }
167 
~ConsumerManagementService()168 ConsumerManagementService::~ConsumerManagementService() {
169   if (enrolling_profile_) {
170     ProfileOAuth2TokenServiceFactory::GetForProfile(enrolling_profile_)->
171         RemoveObserver(this);
172   }
173   if (device_settings_service_)
174     device_settings_service_->RemoveObserver(this);
175   registrar_.Remove(this,
176                     chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
177                     content::NotificationService::AllSources());
178 }
179 
180 // static
RegisterPrefs(PrefRegistrySimple * registry)181 void ConsumerManagementService::RegisterPrefs(PrefRegistrySimple* registry) {
182   registry->RegisterIntegerPref(
183       prefs::kConsumerManagementEnrollmentStage, ENROLLMENT_STAGE_NONE);
184 }
185 
AddObserver(Observer * observer)186 void ConsumerManagementService::AddObserver(Observer* observer) {
187   observers_.AddObserver(observer);
188 }
189 
RemoveObserver(Observer * observer)190 void ConsumerManagementService::RemoveObserver(Observer* observer) {
191   observers_.RemoveObserver(observer);
192 }
193 
194 ConsumerManagementService::Status
GetStatus() const195 ConsumerManagementService::GetStatus() const {
196   if (!device_settings_service_)
197     return STATUS_UNKNOWN;
198 
199   const enterprise_management::PolicyData* policy_data =
200       device_settings_service_->policy_data();
201   if (!policy_data)
202     return STATUS_UNKNOWN;
203 
204   if (policy_data->management_mode() == em::PolicyData::CONSUMER_MANAGED) {
205     // TODO(davidyu): Check if unenrollment is in progress.
206     // http://crbug.com/353050.
207     return STATUS_ENROLLED;
208   }
209 
210   EnrollmentStage stage = GetEnrollmentStage();
211   if (stage > ENROLLMENT_STAGE_NONE && stage < ENROLLMENT_STAGE_SUCCESS)
212     return STATUS_ENROLLING;
213 
214   return STATUS_UNENROLLED;
215 }
216 
GetStatusString() const217 std::string ConsumerManagementService::GetStatusString() const {
218   return kStatusString[GetStatus()];
219 }
220 
221 ConsumerManagementService::EnrollmentStage
GetEnrollmentStage() const222 ConsumerManagementService::GetEnrollmentStage() const {
223   const PrefService* prefs = g_browser_process->local_state();
224   int stage = prefs->GetInteger(prefs::kConsumerManagementEnrollmentStage);
225   if (stage < 0 || stage >= ENROLLMENT_STAGE_LAST) {
226     LOG(ERROR) << "Unknown enrollment stage: " << stage;
227     stage = 0;
228   }
229   return static_cast<EnrollmentStage>(stage);
230 }
231 
SetEnrollmentStage(EnrollmentStage stage)232 void ConsumerManagementService::SetEnrollmentStage(EnrollmentStage stage) {
233   PrefService* prefs = g_browser_process->local_state();
234   prefs->SetInteger(prefs::kConsumerManagementEnrollmentStage, stage);
235 
236   NotifyStatusChanged();
237 }
238 
GetOwner(const GetOwnerCallback & callback)239 void ConsumerManagementService::GetOwner(const GetOwnerCallback& callback) {
240   cryptohome::GetBootAttributeRequest request;
241   request.set_name(kAttributeOwnerId);
242   client_->GetBootAttribute(
243       request,
244       base::Bind(&ConsumerManagementService::OnGetBootAttributeDone,
245                  weak_ptr_factory_.GetWeakPtr(),
246                  callback));
247 }
248 
SetOwner(const std::string & user_id,const SetOwnerCallback & callback)249 void ConsumerManagementService::SetOwner(const std::string& user_id,
250                                          const SetOwnerCallback& callback) {
251   cryptohome::SetBootAttributeRequest request;
252   request.set_name(kAttributeOwnerId);
253   request.set_value(user_id.data(), user_id.size());
254   client_->SetBootAttribute(
255       request,
256       base::Bind(&ConsumerManagementService::OnSetBootAttributeDone,
257                  weak_ptr_factory_.GetWeakPtr(),
258                  callback));
259 }
260 
OwnershipStatusChanged()261 void ConsumerManagementService::OwnershipStatusChanged() {
262 }
263 
DeviceSettingsUpdated()264 void ConsumerManagementService::DeviceSettingsUpdated() {
265   NotifyStatusChanged();
266 }
267 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)268 void ConsumerManagementService::Observe(
269     int type,
270     const content::NotificationSource& source,
271     const content::NotificationDetails& details) {
272   if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) {
273     NOTREACHED() << "Unexpected notification " << type;
274     return;
275   }
276 
277   Profile* profile = content::Details<Profile>(details).ptr();
278   if (chromeos::ProfileHelper::IsOwnerProfile(profile))
279     OnOwnerSignin(profile);
280 }
281 
OnRefreshTokenAvailable(const std::string & account_id)282 void ConsumerManagementService::OnRefreshTokenAvailable(
283     const std::string& account_id) {
284   CHECK(enrolling_profile_);
285 
286   if (account_id == GetAccountIdFromProfile(enrolling_profile_)) {
287     ProfileOAuth2TokenServiceFactory::GetForProfile(enrolling_profile_)->
288         RemoveObserver(this);
289     OnOwnerRefreshTokenAvailable();
290   }
291 }
292 
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)293 void ConsumerManagementService::OnGetTokenSuccess(
294       const OAuth2TokenService::Request* request,
295       const std::string& access_token,
296       const base::Time& expiration_time) {
297   DCHECK_EQ(token_request_, request);
298   base::MessageLoop::current()->DeleteSoon(FROM_HERE, token_request_.release());
299 
300   OnOwnerAccessTokenAvailable(access_token);
301 }
302 
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)303 void ConsumerManagementService::OnGetTokenFailure(
304       const OAuth2TokenService::Request* request,
305       const GoogleServiceAuthError& error) {
306   DCHECK_EQ(token_request_, request);
307   base::MessageLoop::current()->DeleteSoon(FROM_HERE, token_request_.release());
308 
309   LOG(ERROR) << "Failed to get the access token: " << error.ToString();
310   EndEnrollment(ENROLLMENT_STAGE_GET_TOKEN_FAILED);
311 }
312 
OnGetBootAttributeDone(const GetOwnerCallback & callback,chromeos::DBusMethodCallStatus call_status,bool dbus_success,const cryptohome::BaseReply & reply)313 void ConsumerManagementService::OnGetBootAttributeDone(
314     const GetOwnerCallback& callback,
315     chromeos::DBusMethodCallStatus call_status,
316     bool dbus_success,
317     const cryptohome::BaseReply& reply) {
318   if (!dbus_success || reply.error() != 0) {
319     LOG(ERROR) << "Failed to get the owner info from boot lockbox.";
320     callback.Run("");
321     return;
322   }
323 
324   callback.Run(
325       reply.GetExtension(cryptohome::GetBootAttributeReply::reply).value());
326 }
327 
OnSetBootAttributeDone(const SetOwnerCallback & callback,chromeos::DBusMethodCallStatus call_status,bool dbus_success,const cryptohome::BaseReply & reply)328 void ConsumerManagementService::OnSetBootAttributeDone(
329     const SetOwnerCallback& callback,
330     chromeos::DBusMethodCallStatus call_status,
331     bool dbus_success,
332     const cryptohome::BaseReply& reply) {
333   if (!dbus_success || reply.error() != 0) {
334     LOG(ERROR) << "Failed to set owner info in boot lockbox.";
335     callback.Run(false);
336     return;
337   }
338 
339   cryptohome::FlushAndSignBootAttributesRequest request;
340   client_->FlushAndSignBootAttributes(
341       request,
342       base::Bind(&ConsumerManagementService::OnFlushAndSignBootAttributesDone,
343                  weak_ptr_factory_.GetWeakPtr(),
344                  callback));
345 }
346 
OnFlushAndSignBootAttributesDone(const SetOwnerCallback & callback,chromeos::DBusMethodCallStatus call_status,bool dbus_success,const cryptohome::BaseReply & reply)347 void ConsumerManagementService::OnFlushAndSignBootAttributesDone(
348     const SetOwnerCallback& callback,
349     chromeos::DBusMethodCallStatus call_status,
350     bool dbus_success,
351     const cryptohome::BaseReply& reply) {
352   if (!dbus_success || reply.error() != 0) {
353     LOG(ERROR) << "Failed to flush and sign boot lockbox.";
354     callback.Run(false);
355     return;
356   }
357 
358   callback.Run(true);
359 }
360 
OnOwnerSignin(Profile * profile)361 void ConsumerManagementService::OnOwnerSignin(Profile* profile) {
362   const EnrollmentStage stage = GetEnrollmentStage();
363   switch (stage) {
364     case ENROLLMENT_STAGE_NONE:
365       // Do nothing.
366       return;
367 
368     case ENROLLMENT_STAGE_OWNER_STORED:
369       // Continue the enrollment process after the owner signs in.
370       ContinueEnrollmentProcess(profile);
371       return;
372 
373     case ENROLLMENT_STAGE_SUCCESS:
374     case ENROLLMENT_STAGE_CANCELED:
375     case ENROLLMENT_STAGE_BOOT_LOCKBOX_FAILED:
376     case ENROLLMENT_STAGE_DM_SERVER_FAILED:
377     case ENROLLMENT_STAGE_GET_TOKEN_FAILED:
378       ShowDesktopNotificationAndResetStage(stage, profile);
379       return;
380 
381     case ENROLLMENT_STAGE_REQUESTED:
382     case ENROLLMENT_STAGE_LAST:
383       NOTREACHED() << "Unexpected enrollment stage " << stage;
384       return;
385   }
386 }
387 
ContinueEnrollmentProcess(Profile * profile)388 void ConsumerManagementService::ContinueEnrollmentProcess(Profile* profile) {
389   enrolling_profile_ = profile;
390 
391   // First, we need to ensure that the refresh token is available.
392   const std::string& account_id = GetAccountIdFromProfile(profile);
393   ProfileOAuth2TokenService* token_service =
394       ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
395   if (token_service->RefreshTokenIsAvailable(account_id)) {
396     OnOwnerRefreshTokenAvailable();
397   } else {
398     token_service->AddObserver(this);
399   }
400 }
401 
OnOwnerRefreshTokenAvailable()402 void ConsumerManagementService::OnOwnerRefreshTokenAvailable() {
403   CHECK(enrolling_profile_);
404 
405   // Now we can request the OAuth access token for device management to send the
406   // device registration request to the device management server.
407   OAuth2TokenService::ScopeSet oauth_scopes;
408   oauth_scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
409   const std::string& account_id = GetAccountIdFromProfile(enrolling_profile_);
410   token_request_ = ProfileOAuth2TokenServiceFactory::GetForProfile(
411       enrolling_profile_)->StartRequest(account_id, oauth_scopes, this);
412 }
413 
OnOwnerAccessTokenAvailable(const std::string & access_token)414 void ConsumerManagementService::OnOwnerAccessTokenAvailable(
415     const std::string& access_token) {
416   // Now that we have the access token, we got everything we need to send the
417   // device registration request to the device management server.
418   BrowserPolicyConnectorChromeOS* connector =
419       g_browser_process->platform_part()->browser_policy_connector_chromeos();
420   DeviceCloudPolicyInitializer* initializer =
421       connector->GetDeviceCloudPolicyInitializer();
422   CHECK(initializer);
423 
424   policy::DeviceCloudPolicyInitializer::AllowedDeviceModes device_modes;
425   device_modes[policy::DEVICE_MODE_ENTERPRISE] = true;
426 
427   initializer->StartEnrollment(
428       enterprise_management::PolicyData::ENTERPRISE_MANAGED,
429       connector->GetDeviceManagementServiceForConsumer(),
430       access_token,
431       false,  // is_auto_enrollment
432       device_modes,
433       base::Bind(&ConsumerManagementService::OnEnrollmentCompleted,
434                  weak_ptr_factory_.GetWeakPtr()));
435 }
436 
OnEnrollmentCompleted(EnrollmentStatus status)437 void ConsumerManagementService::OnEnrollmentCompleted(EnrollmentStatus status) {
438   if (status.status() != EnrollmentStatus::STATUS_SUCCESS) {
439     LOG(ERROR) << "Failed to enroll the device."
440                << " status=" << status.status()
441                << " client_status=" << status.client_status()
442                << " http_status=" << status.http_status()
443                << " store_status=" << status.store_status()
444                << " validation_status=" << status.validation_status();
445     EndEnrollment(ENROLLMENT_STAGE_DM_SERVER_FAILED);
446     return;
447   }
448 
449   EndEnrollment(ENROLLMENT_STAGE_SUCCESS);
450 }
451 
EndEnrollment(EnrollmentStage stage)452 void ConsumerManagementService::EndEnrollment(EnrollmentStage stage) {
453   Profile* profile = enrolling_profile_;
454   enrolling_profile_ = NULL;
455 
456   SetEnrollmentStage(stage);
457   if (user_manager::UserManager::Get()->IsCurrentUserOwner())
458     ShowDesktopNotificationAndResetStage(stage, profile);
459 }
460 
ShowDesktopNotificationAndResetStage(EnrollmentStage stage,Profile * profile)461 void ConsumerManagementService::ShowDesktopNotificationAndResetStage(
462     EnrollmentStage stage, Profile* profile) {
463   base::string16 title;
464   base::string16 body;
465   base::string16 button_label;
466   base::Closure button_click_callback;
467 
468   if (stage == ENROLLMENT_STAGE_SUCCESS) {
469     title = l10n_util::GetStringUTF16(
470         IDS_CONSUMER_MANAGEMENT_ENROLLMENT_NOTIFICATION_TITLE);
471     body = l10n_util::GetStringUTF16(
472         IDS_CONSUMER_MANAGEMENT_ENROLLMENT_NOTIFICATION_BODY);
473     button_label = l10n_util::GetStringUTF16(
474         IDS_CONSUMER_MANAGEMENT_NOTIFICATION_MODIFY_SETTINGS_BUTTON);
475     button_click_callback = base::Bind(
476         &ConsumerManagementService::OpenSettingsPage,
477         weak_ptr_factory_.GetWeakPtr(),
478         profile);
479   } else {
480     title = l10n_util::GetStringUTF16(
481         IDS_CONSUMER_MANAGEMENT_ENROLLMENT_FAILURE_NOTIFICATION_TITLE);
482     body = l10n_util::GetStringUTF16(
483         IDS_CONSUMER_MANAGEMENT_ENROLLMENT_FAILURE_NOTIFICATION_BODY);
484     button_label = l10n_util::GetStringUTF16(
485         IDS_CONSUMER_MANAGEMENT_NOTIFICATION_TRY_AGAIN_BUTTON);
486     button_click_callback = base::Bind(
487         &ConsumerManagementService::TryEnrollmentAgain,
488         weak_ptr_factory_.GetWeakPtr(),
489         profile);
490   }
491 
492   message_center::RichNotificationData optional_field;
493   optional_field.buttons.push_back(message_center::ButtonInfo(button_label));
494   Notification notification(
495       message_center::NOTIFICATION_TYPE_SIMPLE,
496       GURL(kEnrollmentNotificationUrl),
497       title,
498       body,
499       ui::ResourceBundle::GetSharedInstance().GetImageNamed(
500           IDR_CONSUMER_MANAGEMENT_NOTIFICATION_ICON),
501       blink::WebTextDirectionDefault,
502       message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
503                                  kEnrollmentNotificationId),
504       base::string16(),  // display_source
505       base::UTF8ToUTF16(kEnrollmentNotificationId),
506       optional_field,
507       new DesktopNotificationDelegate(kEnrollmentNotificationId,
508                                       button_click_callback));
509   notification.SetSystemPriority();
510   g_browser_process->notification_ui_manager()->Add(notification, profile);
511 
512   SetEnrollmentStage(ENROLLMENT_STAGE_NONE);
513 }
514 
OpenSettingsPage(Profile * profile) const515 void ConsumerManagementService::OpenSettingsPage(Profile* profile) const {
516   const GURL url(chrome::kChromeUISettingsURL);
517   chrome::NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK);
518   params.disposition = NEW_FOREGROUND_TAB;
519   chrome::Navigate(&params);
520 }
521 
TryEnrollmentAgain(Profile * profile) const522 void ConsumerManagementService::TryEnrollmentAgain(Profile* profile) const {
523   const GURL base_url(chrome::kChromeUISettingsURL);
524   const GURL url = base_url.Resolve(kConsumerManagementOverlay);
525 
526   chrome::NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK);
527   params.disposition = NEW_FOREGROUND_TAB;
528   chrome::Navigate(&params);
529 }
530 
NotifyStatusChanged()531 void ConsumerManagementService::NotifyStatusChanged() {
532   FOR_EACH_OBSERVER(Observer, observers_, OnConsumerManagementStatusChanged());
533 }
534 
535 }  // namespace policy
536