15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.h"
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <string>
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/bind.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/bind_helpers.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/location.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/logging.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/prefs/pref_service.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/time/clock.h"
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/time/time.h"
16116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chrome/browser/chromeos/profiles/profile_helper.h"
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/pref_names.h"
19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "components/pref_registry/pref_registry_syncable.h"
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "components/user_manager/user.h"
216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "components/user_manager/user_manager.h"
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace chromeos {
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int kDefaultSAMLOfflineSigninTimeLimit = 14 * 24 * 60 * 60;  // 14 days.
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SAMLOfflineSigninLimiter::RegisterProfilePrefs(
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    user_prefs::PrefRegistrySyncable* registry) {
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  registry->RegisterIntegerPref(
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      prefs::kSAMLOfflineSigninTimeLimit,
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      kDefaultSAMLOfflineSigninTimeLimit,
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  registry->RegisterInt64Pref(
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      prefs::kSAMLLastGAIASignInTime,
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      0,
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SAMLOfflineSigninLimiter::SignedIn(UserContext::AuthFlow auth_flow) {
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PrefService* prefs = profile_->GetPrefs();
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  const user_manager::User* user =
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      ProfileHelper::Get()->GetUserByProfile(profile_);
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!user) {
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    NOTREACHED();
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& user_id = user->email();
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (auth_flow == UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML) {
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // The user went through online authentication and GAIA did not redirect to
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // a SAML IdP. No limit applies in this case. Clear the time of last login
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // with SAML and the flag enforcing online login, then return.
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    prefs->ClearPref(prefs::kSAMLLastGAIASignInTime);
596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    user_manager::UserManager::Get()->SaveForceOnlineSignin(user_id, false);
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (auth_flow == UserContext::AUTH_FLOW_GAIA_WITH_SAML) {
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // The user went through online authentication and GAIA did redirect to a
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // SAML IdP. Update the time of last login with SAML and clear the flag
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // enforcing online login. The flag will be set again when the limit
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // expires. If the limit already expired (e.g. because it was set to zero),
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // the flag will be set again immediately.
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    user_manager::UserManager::Get()->SaveForceOnlineSignin(user_id, false);
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    prefs->SetInt64(prefs::kSAMLLastGAIASignInTime,
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    clock_->Now().ToInternalValue());
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Start listening for pref changes.
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  pref_change_registrar_.Init(prefs);
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  pref_change_registrar_.Add(prefs::kSAMLOfflineSigninTimeLimit,
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             base::Bind(&SAMLOfflineSigninLimiter::UpdateLimit,
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                        base::Unretained(this)));
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Arm the |offline_signin_limit_timer_| if a limit is in force.
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UpdateLimit();
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SAMLOfflineSigninLimiter::Shutdown() {
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  pref_change_registrar_.RemoveAll();
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  offline_signin_limit_timer_.reset();
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SAMLOfflineSigninLimiter::SAMLOfflineSigninLimiter(Profile* profile,
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                                   base::Clock* clock)
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : profile_(profile),
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      clock_(clock ? clock : &default_clock_) {
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SAMLOfflineSigninLimiter::~SAMLOfflineSigninLimiter() {
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SAMLOfflineSigninLimiter::UpdateLimit() {
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Stop the |offline_signin_limit_timer_|.
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  offline_signin_limit_timer_.reset();
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PrefService* prefs = pref_change_registrar_.prefs();
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const base::TimeDelta offline_signin_time_limit =
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::TimeDelta::FromSeconds(
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          prefs->GetInteger(prefs::kSAMLOfflineSigninTimeLimit));
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::Time last_gaia_signin_time = base::Time::FromInternalValue(
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      prefs->GetInt64(prefs::kSAMLLastGAIASignInTime));
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (offline_signin_time_limit < base::TimeDelta() ||
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      last_gaia_signin_time.is_null()) {
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If no limit is in force, return.
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const base::Time now = clock_->Now();
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (last_gaia_signin_time > now) {
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If the time of last login with SAML lies in the future, set it to the
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // current time.
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    NOTREACHED();
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    last_gaia_signin_time = now;
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    prefs->SetInt64(prefs::kSAMLLastGAIASignInTime, now.ToInternalValue());
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const base::TimeDelta time_since_last_gaia_signin =
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      now - last_gaia_signin_time;
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (time_since_last_gaia_signin >= offline_signin_time_limit) {
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If the limit already expired, set the flag enforcing online login
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // immediately and return.
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ForceOnlineLogin();
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Arm |offline_signin_limit_timer_| so that it sets the flag enforcing online
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // login when the limit expires.
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  offline_signin_limit_timer_.reset(
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      new base::OneShotTimer<SAMLOfflineSigninLimiter>);
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  offline_signin_limit_timer_->Start(
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FROM_HERE,
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      offline_signin_time_limit - time_since_last_gaia_signin,
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this,
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      &SAMLOfflineSigninLimiter::ForceOnlineLogin);
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SAMLOfflineSigninLimiter::ForceOnlineLogin() {
1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile_);
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!user) {
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    NOTREACHED();
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  user_manager::UserManager::Get()->SaveForceOnlineSignin(user->email(), true);
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  offline_signin_limit_timer_.reset();
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace chromeos
155