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/login/saml/saml_offline_signin_limiter.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/location.h"
12#include "base/logging.h"
13#include "base/prefs/pref_service.h"
14#include "base/time/clock.h"
15#include "base/time/time.h"
16#include "chrome/browser/chromeos/profiles/profile_helper.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/common/pref_names.h"
19#include "components/pref_registry/pref_registry_syncable.h"
20#include "components/user_manager/user.h"
21#include "components/user_manager/user_manager.h"
22
23namespace chromeos {
24
25namespace {
26
27const int kDefaultSAMLOfflineSigninTimeLimit = 14 * 24 * 60 * 60;  // 14 days.
28
29}  // namespace
30
31// static
32void SAMLOfflineSigninLimiter::RegisterProfilePrefs(
33    user_prefs::PrefRegistrySyncable* registry) {
34  registry->RegisterIntegerPref(
35      prefs::kSAMLOfflineSigninTimeLimit,
36      kDefaultSAMLOfflineSigninTimeLimit,
37      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
38  registry->RegisterInt64Pref(
39      prefs::kSAMLLastGAIASignInTime,
40      0,
41      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
42}
43
44void SAMLOfflineSigninLimiter::SignedIn(UserContext::AuthFlow auth_flow) {
45  PrefService* prefs = profile_->GetPrefs();
46  const user_manager::User* user =
47      ProfileHelper::Get()->GetUserByProfile(profile_);
48  if (!user) {
49    NOTREACHED();
50    return;
51  }
52  const std::string& user_id = user->email();
53
54  if (auth_flow == UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML) {
55    // The user went through online authentication and GAIA did not redirect to
56    // a SAML IdP. No limit applies in this case. Clear the time of last login
57    // with SAML and the flag enforcing online login, then return.
58    prefs->ClearPref(prefs::kSAMLLastGAIASignInTime);
59    user_manager::UserManager::Get()->SaveForceOnlineSignin(user_id, false);
60    return;
61  }
62
63  if (auth_flow == UserContext::AUTH_FLOW_GAIA_WITH_SAML) {
64    // The user went through online authentication and GAIA did redirect to a
65    // SAML IdP. Update the time of last login with SAML and clear the flag
66    // enforcing online login. The flag will be set again when the limit
67    // expires. If the limit already expired (e.g. because it was set to zero),
68    // the flag will be set again immediately.
69    user_manager::UserManager::Get()->SaveForceOnlineSignin(user_id, false);
70    prefs->SetInt64(prefs::kSAMLLastGAIASignInTime,
71                    clock_->Now().ToInternalValue());
72  }
73
74  // Start listening for pref changes.
75  pref_change_registrar_.Init(prefs);
76  pref_change_registrar_.Add(prefs::kSAMLOfflineSigninTimeLimit,
77                             base::Bind(&SAMLOfflineSigninLimiter::UpdateLimit,
78                                        base::Unretained(this)));
79
80  // Arm the |offline_signin_limit_timer_| if a limit is in force.
81  UpdateLimit();
82}
83
84void SAMLOfflineSigninLimiter::Shutdown() {
85  pref_change_registrar_.RemoveAll();
86  offline_signin_limit_timer_.reset();
87}
88
89SAMLOfflineSigninLimiter::SAMLOfflineSigninLimiter(Profile* profile,
90                                                   base::Clock* clock)
91    : profile_(profile),
92      clock_(clock ? clock : &default_clock_) {
93}
94
95SAMLOfflineSigninLimiter::~SAMLOfflineSigninLimiter() {
96}
97
98void SAMLOfflineSigninLimiter::UpdateLimit() {
99  // Stop the |offline_signin_limit_timer_|.
100  offline_signin_limit_timer_.reset();
101
102  PrefService* prefs = pref_change_registrar_.prefs();
103  const base::TimeDelta offline_signin_time_limit =
104      base::TimeDelta::FromSeconds(
105          prefs->GetInteger(prefs::kSAMLOfflineSigninTimeLimit));
106  base::Time last_gaia_signin_time = base::Time::FromInternalValue(
107      prefs->GetInt64(prefs::kSAMLLastGAIASignInTime));
108  if (offline_signin_time_limit < base::TimeDelta() ||
109      last_gaia_signin_time.is_null()) {
110    // If no limit is in force, return.
111    return;
112  }
113
114  const base::Time now = clock_->Now();
115  if (last_gaia_signin_time > now) {
116    // If the time of last login with SAML lies in the future, set it to the
117    // current time.
118    NOTREACHED();
119    last_gaia_signin_time = now;
120    prefs->SetInt64(prefs::kSAMLLastGAIASignInTime, now.ToInternalValue());
121  }
122
123  const base::TimeDelta time_since_last_gaia_signin =
124      now - last_gaia_signin_time;
125  if (time_since_last_gaia_signin >= offline_signin_time_limit) {
126    // If the limit already expired, set the flag enforcing online login
127    // immediately and return.
128    ForceOnlineLogin();
129    return;
130  }
131
132  // Arm |offline_signin_limit_timer_| so that it sets the flag enforcing online
133  // login when the limit expires.
134  offline_signin_limit_timer_.reset(
135      new base::OneShotTimer<SAMLOfflineSigninLimiter>);
136  offline_signin_limit_timer_->Start(
137      FROM_HERE,
138      offline_signin_time_limit - time_since_last_gaia_signin,
139      this,
140      &SAMLOfflineSigninLimiter::ForceOnlineLogin);
141}
142
143void SAMLOfflineSigninLimiter::ForceOnlineLogin() {
144  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile_);
145  if (!user) {
146    NOTREACHED();
147    return;
148  }
149
150  user_manager::UserManager::Get()->SaveForceOnlineSignin(user->email(), true);
151  offline_signin_limit_timer_.reset();
152}
153
154}  // namespace chromeos
155