35abeb32ff13107673ffd8f3349500614de7f9ec
[platform/framework/web/crosswalk.git] / src / chrome / browser / signin / easy_unlock_service_signin_chromeos.cc
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/signin/easy_unlock_service_signin_chromeos.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
15 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
16 #include "chromeos/login/auth/user_context.h"
17
18 namespace {
19
20 // The maximum allowed backoff interval when waiting for cryptohome to start.
21 uint32 kMaxCryptohomeBackoffIntervalMs = 10000u;
22
23 // If the data load fails, the initial interval after which the load will be
24 // retried. Further intervals will exponentially increas by factor 2.
25 uint32 kInitialCryptohomeBackoffIntervalMs = 200u;
26
27 // Calculates the backoff interval that should be used next.
28 // |backoff| The last backoff interval used.
29 uint32 GetNextBackoffInterval(uint32 backoff) {
30   if (backoff == 0u)
31     return kInitialCryptohomeBackoffIntervalMs;
32   return backoff * 2;
33 }
34
35 void LoadDataForUser(
36     const std::string& user_id,
37     uint32 backoff_ms,
38     const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback);
39
40 // Callback passed to |LoadDataForUser()|.
41 // If |LoadDataForUser| function succeeded, it invokes |callback| with the
42 // results.
43 // If |LoadDataForUser| failed and further retries are allowed, schedules new
44 // |LoadDataForUser| call with some backoff. If no further retires are allowed,
45 // it invokes |callback| with the |LoadDataForUser| results.
46 void RetryDataLoadOnError(
47     const std::string& user_id,
48     uint32 backoff_ms,
49     const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback,
50     bool success,
51     const chromeos::EasyUnlockDeviceKeyDataList& data_list) {
52   if (success) {
53     callback.Run(success, data_list);
54     return;
55   }
56
57   uint32 next_backoff_ms = GetNextBackoffInterval(backoff_ms);
58   if (next_backoff_ms > kMaxCryptohomeBackoffIntervalMs) {
59     callback.Run(false, data_list);
60     return;
61   }
62
63   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
64       FROM_HERE,
65       base::Bind(&LoadDataForUser, user_id, next_backoff_ms, callback),
66       base::TimeDelta::FromMilliseconds(next_backoff_ms));
67 }
68
69 // Loads device data list associated with the user's Easy unlock keys.
70 void LoadDataForUser(
71     const std::string& user_id,
72     uint32 backoff_ms,
73     const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback) {
74   chromeos::EasyUnlockKeyManager* key_manager =
75       chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
76   DCHECK(key_manager);
77
78   key_manager->GetDeviceDataList(
79       chromeos::UserContext(user_id),
80       base::Bind(&RetryDataLoadOnError, user_id, backoff_ms, callback));
81 }
82
83 }  // namespace
84
85 EasyUnlockServiceSignin::UserData::UserData()
86     : state(EasyUnlockServiceSignin::USER_DATA_STATE_INITIAL) {
87 }
88
89 EasyUnlockServiceSignin::UserData::~UserData() {}
90
91 EasyUnlockServiceSignin::EasyUnlockServiceSignin(Profile* profile)
92     : EasyUnlockService(profile),
93       allow_cryptohome_backoff_(true),
94       service_active_(false),
95       weak_ptr_factory_(this) {
96 }
97
98 EasyUnlockServiceSignin::~EasyUnlockServiceSignin() {
99 }
100
101 EasyUnlockService::Type EasyUnlockServiceSignin::GetType() const {
102   return EasyUnlockService::TYPE_SIGNIN;
103 }
104
105 std::string EasyUnlockServiceSignin::GetUserEmail() const {
106   return user_id_;
107 }
108
109 void EasyUnlockServiceSignin::LaunchSetup() {
110   NOTREACHED();
111 }
112
113 const base::DictionaryValue* EasyUnlockServiceSignin::GetPermitAccess() const {
114   return NULL;
115 }
116
117 void EasyUnlockServiceSignin::SetPermitAccess(
118     const base::DictionaryValue& permit) {
119   NOTREACHED();
120 }
121
122 void EasyUnlockServiceSignin::ClearPermitAccess() {
123   NOTREACHED();
124 }
125
126 const base::ListValue* EasyUnlockServiceSignin::GetRemoteDevices() const {
127   const UserData* data = FindLoadedDataForCurrentUser();
128   if (!data)
129     return NULL;
130   return &data->remote_devices_value;
131 }
132
133 void EasyUnlockServiceSignin::SetRemoteDevices(
134     const base::ListValue& devices) {
135   NOTREACHED();
136 }
137
138 void EasyUnlockServiceSignin::ClearRemoteDevices() {
139   NOTREACHED();
140 }
141
142 void EasyUnlockServiceSignin::RunTurnOffFlow() {
143   NOTREACHED();
144 }
145
146 void EasyUnlockServiceSignin::ResetTurnOffFlow() {
147   NOTREACHED();
148 }
149
150 EasyUnlockService::TurnOffFlowStatus
151     EasyUnlockServiceSignin::GetTurnOffFlowStatus() const {
152   return EasyUnlockService::IDLE;
153 }
154
155 std::string EasyUnlockServiceSignin::GetChallenge() const {
156   const UserData* data = FindLoadedDataForCurrentUser();
157   // TODO(xiyuan): Use correct remote device instead of hard coded first one.
158   uint32 device_index = 0;
159   if (!data || data->devices.size() <= device_index)
160     return std::string();
161   return data->devices[device_index].challenge;
162 }
163
164 std::string EasyUnlockServiceSignin::GetWrappedSecret() const {
165   const UserData* data = FindLoadedDataForCurrentUser();
166   // TODO(xiyuan): Use correct remote device instead of hard coded first one.
167   uint32 device_index = 0;
168   if (!data || data->devices.size() <= device_index)
169     return std::string();
170   return data->devices[device_index].wrapped_secret;
171 }
172
173 void EasyUnlockServiceSignin::InitializeInternal() {
174   if (chromeos::LoginState::Get()->IsUserLoggedIn())
175     return;
176
177   service_active_ = true;
178
179   chromeos::LoginState::Get()->AddObserver(this);
180   ScreenlockBridge* screenlock_bridge = ScreenlockBridge::Get();
181   screenlock_bridge->AddObserver(this);
182   if (!screenlock_bridge->focused_user_id().empty())
183     OnFocusedUserChanged(screenlock_bridge->focused_user_id());
184 }
185
186 void EasyUnlockServiceSignin::ShutdownInternal() {
187   if (!service_active_)
188     return;
189   service_active_ = false;
190
191   weak_ptr_factory_.InvalidateWeakPtrs();
192   ScreenlockBridge::Get()->RemoveObserver(this);
193   chromeos::LoginState::Get()->RemoveObserver(this);
194   STLDeleteContainerPairSecondPointers(user_data_.begin(), user_data_.end());
195   user_data_.clear();
196 }
197
198 bool EasyUnlockServiceSignin::IsAllowedInternal() {
199   return service_active_ &&
200          !user_id_.empty() &&
201          !chromeos::LoginState::Get()->IsUserLoggedIn();
202 }
203
204 void EasyUnlockServiceSignin::OnScreenDidLock() {
205 }
206
207 void EasyUnlockServiceSignin::OnScreenDidUnlock() {
208 }
209
210 void EasyUnlockServiceSignin::OnFocusedUserChanged(const std::string& user_id) {
211   if (user_id_ == user_id)
212     return;
213
214   // Setting or clearing the user_id may changed |IsAllowed| value, so in these
215   // cases update the app state. Otherwise, it's enough to notify the app the
216   // user data has been updated.
217   bool should_update_app_state = user_id_.empty() != user_id.empty();
218   user_id_ = user_id;
219
220   ResetScreenlockState();
221
222   if (should_update_app_state) {
223     UpdateAppState();
224   } else {
225     NotifyUserUpdated();
226   }
227
228   LoadCurrentUserDataIfNeeded();
229 }
230
231 void EasyUnlockServiceSignin::LoggedInStateChanged() {
232   if (!chromeos::LoginState::Get()->IsUserLoggedIn())
233     return;
234   UnloadApp();
235   Shutdown();
236 }
237
238 void EasyUnlockServiceSignin::LoadCurrentUserDataIfNeeded() {
239   if (user_id_.empty() || !service_active_)
240     return;
241
242   std::map<std::string, UserData*>::iterator it = user_data_.find(user_id_);
243   if (it == user_data_.end())
244     user_data_.insert(std::make_pair(user_id_, new UserData()));
245
246   UserData* data = user_data_[user_id_];
247
248   if (data->state != USER_DATA_STATE_INITIAL)
249     return;
250   data->state = USER_DATA_STATE_LOADING;
251
252   LoadDataForUser(
253       user_id_,
254       allow_cryptohome_backoff_ ? 0u : kMaxCryptohomeBackoffIntervalMs,
255       base::Bind(&EasyUnlockServiceSignin::OnUserDataLoaded,
256                  weak_ptr_factory_.GetWeakPtr(),
257                  user_id_));
258 }
259
260 void EasyUnlockServiceSignin::OnUserDataLoaded(
261     const std::string& user_id,
262     bool success,
263     const chromeos::EasyUnlockDeviceKeyDataList& devices) {
264   allow_cryptohome_backoff_ = false;
265
266   UserData* data = user_data_[user_id_];
267   data->state = USER_DATA_STATE_LOADED;
268   if (success) {
269     data->devices = devices;
270     chromeos::EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList(
271         user_id, devices, &data->remote_devices_value);
272   }
273
274   // If the fetched data belongs to the currently focused user, notify the app
275   // that it has to refresh it's user data.
276   if (user_id == user_id_)
277     NotifyUserUpdated();
278 }
279
280 const EasyUnlockServiceSignin::UserData*
281     EasyUnlockServiceSignin::FindLoadedDataForCurrentUser() const {
282   if (user_id_.empty())
283     return NULL;
284
285   std::map<std::string, UserData*>::const_iterator it =
286       user_data_.find(user_id_);
287   if (it == user_data_.end())
288     return NULL;
289   if (it->second->state != USER_DATA_STATE_LOADED)
290     return NULL;
291   return it->second;
292 }