summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/engine/auth_watcher.h
blob: 925d7caf4ae302f126d8b93ea92353050a0273c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Copyright (c) 2006-2009 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.
//
// AuthWatcher watches authentication events and user open and close
// events and accordingly opens and closes shares.

#ifndef CHROME_BROWSER_SYNC_ENGINE_AUTH_WATCHER_H_
#define CHROME_BROWSER_SYNC_ENGINE_AUTH_WATCHER_H_

#include <map>
#include <string>

#include "base/atomicops.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/thread.h"
#include "chrome/browser/sync/engine/net/gaia_authenticator.h"
#include "chrome/browser/sync/protocol/service_constants.h"
#include "chrome/browser/sync/util/sync_types.h"
#include "chrome/common/deprecated/event_sys.h"
#include "testing/gtest/include/gtest/gtest_prod.h"  // For FRIEND_TEST

namespace syncable {
struct DirectoryManagerEvent;
class DirectoryManager;
}

namespace browser_sync {

class AllStatus;
class AuthWatcher;
class ServerConnectionManager;
class TalkMediator;
class URLFactory;
class UserSettings;
struct ServerConnectionEvent;

struct AuthWatcherEvent {
  enum WhatHappened {
    AUTHENTICATION_ATTEMPT_START,
    AUTHWATCHER_DESTROYED,
    AUTH_RENEWED,  // Currently only used in testing.
    AUTH_SUCCEEDED,
    GAIA_AUTH_FAILED,
    SERVICE_USER_NOT_SIGNED_UP,
    SERVICE_AUTH_FAILED,
    SERVICE_CONNECTION_FAILED,
    // Used in a safety check in AuthWatcher::AuthenticateWithToken()
    ILLEGAL_VALUE,
  };
  WhatHappened what_happened;
  const GaiaAuthenticator::AuthResults* auth_results;
  // use AuthWatcherEvent as its own traits type in hookups.
  typedef AuthWatcherEvent EventType;
  static inline bool IsChannelShutdownEvent(const AuthWatcherEvent& event) {
    return event.what_happened == AUTHWATCHER_DESTROYED;
  }

  // Used for AUTH_SUCCEEDED notification
  std::string user_email;

  // How was this auth attempt initiated?
  enum AuthenticationTrigger {
    USER_INITIATED = 0,  // default value.
    EXPIRED_CREDENTIALS,
  };

  AuthenticationTrigger trigger;
};

// The mother-class of Authentication for the sync backend.  Handles both gaia
// and sync service authentication via asynchronous Authenticate* methods,
// raising AuthWatcherEvents on success/failure.  The implementation currently
// runs its own backend thread for the actual auth processing, which means
// the AuthWatcherEvents can be raised on a different thread than the one that
// invoked authentication.
class AuthWatcher : public base::RefCountedThreadSafe<AuthWatcher> {
 friend class AuthWatcherTest;
 FRIEND_TEST(AuthWatcherTest, Construction);
 public:
  // Normal progression is local -> gaia -> token.
  enum Status { LOCALLY_AUTHENTICATED, GAIA_AUTHENTICATED, NOT_AUTHENTICATED };
  typedef syncable::DirectoryManagerEvent DirectoryManagerEvent;
  typedef syncable::DirectoryManager DirectoryManager;

  AuthWatcher(DirectoryManager* dirman,
              ServerConnectionManager* scm,
              AllStatus* allstatus,
              const std::string& user_agent,
              const std::string& service_id,
              const std::string& gaia_url,
              UserSettings* user_settings,
              GaiaAuthenticator* gaia_auth,
              TalkMediator* talk_mediator);
  ~AuthWatcher();

  typedef EventChannel<AuthWatcherEvent, Lock> Channel;

  inline Channel* channel() const {
    return channel_.get();
  }

  // The following 3 flavors of authentication routines are asynchronous and can
  // be called from any thread.
  // If |captcha_value| is specified but |captcha_token| is not, this will
  // attempt authentication using the last observed captcha token out of
  // convenience in the common case so the token doesn't have to be plumbed
  // everywhere.
  void Authenticate(const std::string& email, const std::string& password,
      const std::string& captcha_token, const std::string& captcha_value,
      bool persist_creds_to_disk);

  void Authenticate(const std::string& email, const std::string& password,
      bool persist_creds_to_disk) {
    Authenticate(email, password, "", "", persist_creds_to_disk);
  }

  // Use this to update only the token of the current email address.
  void RenewAuthToken(const std::string& updated_token);
  void DoRenewAuthToken(const std::string& updated_token);

  // Use this version when you don't need the gaia authentication step because
  // you already have a valid LSID cookie for |gaia_email|.
  void AuthenticateWithLsid(const std::string& lsid);

  // Use this version when you don't need the gaia authentication step because
  // you already have a valid token for |gaia_email|.
  void AuthenticateWithToken(const std::string& gaia_email,
                             const std::string& auth_token);

  // Joins on the backend thread.  The AuthWatcher is useless after this and
  // should be destroyed.
  void Shutdown() { auth_backend_thread_.Stop(); }

  std::string email() const;
  syncable::DirectoryManager* dirman() const { return dirman_; }
  ServerConnectionManager* scm() const { return scm_; }
  AllStatus* allstatus() const { return allstatus_; }
  UserSettings* settings() const { return user_settings_; }
  Status status() const { return (Status)status_; }

 protected:
  void ClearAuthenticationData();

  void NotifyAuthSucceeded(const std::string& email);
  void HandleServerConnectionEvent(const ServerConnectionEvent& event);

  void SaveUserSettings(const std::string& username,
                        const std::string& auth_token,
                        const bool save_credentials);

  MessageLoop* message_loop() { return auth_backend_thread_.message_loop(); }

 private:
  // These two helpers should only be called from the auth function.
  // Called when authentication with gaia succeeds, to save credential info.
  void PersistCredentials();
  // Called when authentication with gaia fails.
  void ProcessGaiaAuthFailure();

  // Just checks that the user has at least one local share cache.
  bool AuthenticateLocally(std::string email);
  // Also checks the user's password against stored password hash.
  bool AuthenticateLocally(std::string email, const std::string& password);

  // Sets the trigger member of the event and sends the event on channel_.
  void NotifyListeners(AuthWatcherEvent* event);

  inline std::string FormatAsEmailAddress(const std::string& email) const {
    std::string mail(email);
    if (email.find('@') == std::string::npos) {
      mail.push_back('@');
      // TODO(chron): Should this be done only at the UI level?
      mail.append(DEFAULT_SIGNIN_DOMAIN);
    }
    return mail;
  }

  // A struct to marshal various data across to the auth_backend_thread_ on
  // Authenticate() and AuthenticateWithToken calls.
  struct AuthRequest {
    std::string email;
    std::string password;
    std::string auth_token;
    std::string captcha_token;
    std::string captcha_value;
    bool persist_creds_to_disk;
    AuthWatcherEvent::AuthenticationTrigger trigger;
  };

  // The public interface Authenticate methods are proxies to these, which
  // can only be called from |auth_backend_thread_|.
  void DoAuthenticate(const AuthRequest& request);
  void DoAuthenticateWithLsid(const std::string& lsid);
  void DoAuthenticateWithToken(const std::string& email,
                               const std::string& auth_token);

  // The public HandleServerConnectionEvent method proxies to this method, which
  // can only be called on |auth_backend_thread_|.
  void DoHandleServerConnectionEvent(
      const ServerConnectionEvent& event,
      const std::string& auth_token_snapshot);

  scoped_ptr<GaiaAuthenticator> const gaia_;
  syncable::DirectoryManager* const dirman_;
  ServerConnectionManager* const scm_;
  scoped_ptr<EventListenerHookup> connmgr_hookup_;
  AllStatus* const allstatus_;
  Status status_;
  UserSettings* const user_settings_;
  TalkMediator* talk_mediator_;  // Interface to the notifications engine.
  scoped_ptr<Channel> channel_;

  base::Thread auth_backend_thread_;

  AuthWatcherEvent::AuthenticationTrigger current_attempt_trigger_;
  DISALLOW_COPY_AND_ASSIGN(AuthWatcher);
};

}  // namespace browser_sync

#endif  // CHROME_BROWSER_SYNC_ENGINE_AUTH_WATCHER_H_