Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: utils/pub/oauth2.dart

Issue 12079112: Make a bunch of stuff in pub synchronous. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library oauth2; 5 library oauth2;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:io'; 8 import 'dart:io';
9 import 'dart:uri'; 9 import 'dart:uri';
10 10
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
44 44
45 /// The OAuth2 scopes that the pub client needs. Currently the client only needs 45 /// The OAuth2 scopes that the pub client needs. Currently the client only needs
46 /// the user's email so that the server can verify their identity. 46 /// the user's email so that the server can verify their identity.
47 final _scopes = ['https://www.googleapis.com/auth/userinfo.email']; 47 final _scopes = ['https://www.googleapis.com/auth/userinfo.email'];
48 48
49 /// An in-memory cache of the user's OAuth2 credentials. This should always be 49 /// An in-memory cache of the user's OAuth2 credentials. This should always be
50 /// the same as the credentials file stored in the system cache. 50 /// the same as the credentials file stored in the system cache.
51 Credentials _credentials; 51 Credentials _credentials;
52 52
53 /// Delete the cached credentials, if they exist. 53 /// Delete the cached credentials, if they exist.
54 Future clearCredentials(SystemCache cache) { 54 void clearCredentials(SystemCache cache) {
55 _credentials = null; 55 _credentials = null;
56 var credentialsFile = _credentialsFile(cache); 56 var credentialsFile = _credentialsFile(cache);
57 return fileExists(credentialsFile).then((exists) { 57 if (!fileExists(credentialsFile)) return;
58 if (exists) return deleteFile(credentialsFile); 58
59 }); 59 deleteFile(credentialsFile);
60 } 60 }
61 61
62 /// Asynchronously passes an OAuth2 [Client] to [fn], and closes the client when 62 /// Asynchronously passes an OAuth2 [Client] to [fn], and closes the client when
63 /// the [Future] returned by [fn] completes. 63 /// the [Future] returned by [fn] completes.
64 /// 64 ///
65 /// This takes care of loading and saving the client's credentials, as well as 65 /// This takes care of loading and saving the client's credentials, as well as
66 /// prompting the user for their authorization. It will also re-authorize and 66 /// prompting the user for their authorization. It will also re-authorize and
67 /// re-run [fn] if a recoverable authorization error is detected. 67 /// re-run [fn] if a recoverable authorization error is detected.
68 Future withClient(SystemCache cache, Future fn(Client client)) { 68 Future withClient(SystemCache cache, Future fn(Client client)) {
69 return _getClient(cache).then((client) { 69 // Make sure all errors propagate through future.
70 return defer(() {
71 var client = _getClient(cache);
70 var completer = new Completer(); 72 var completer = new Completer();
71 return fn(client).whenComplete(() { 73 return fn(client).whenComplete(() {
72 client.close(); 74 client.close();
73 // Be sure to save the credentials even when an error happens. 75 // Be sure to save the credentials even when an error happens.
74 return _saveCredentials(cache, client.credentials); 76 _saveCredentials(cache, client.credentials);
75 }); 77 });
76 }).catchError((asyncError) { 78 }).catchError((asyncError) {
77 if (asyncError.error is ExpirationException) { 79 if (asyncError.error is ExpirationException) {
78 log.error("Pub's authorization to upload packages has expired and " 80 log.error("Pub's authorization to upload packages has expired and "
79 "can't be automatically refreshed."); 81 "can't be automatically refreshed.");
80 return withClient(cache, fn); 82 return withClient(cache, fn);
81 } else if (asyncError.error is AuthorizationException) { 83 } else if (asyncError.error is AuthorizationException) {
82 var message = "OAuth2 authorization failed"; 84 var message = "OAuth2 authorization failed";
83 if (asyncError.error.description != null) { 85 if (asyncError.error.description != null) {
84 message = "$message (${asyncError.error.description})"; 86 message = "$message (${asyncError.error.description})";
85 } 87 }
86 log.error("$message."); 88 log.error("$message.");
87 return clearCredentials(cache).then((_) => withClient(cache, fn)); 89 clearCredentials(cache);
90 return withClient(cache, fn);
88 } else { 91 } else {
89 throw asyncError; 92 throw asyncError;
90 } 93 }
91 }); 94 });
92 } 95 }
93 96
94 /// Gets a new OAuth2 client. If saved credentials are available, those are 97 /// Gets a new OAuth2 client. If saved credentials are available, those are
95 /// used; otherwise, the user is prompted to authorize the pub client. 98 /// used; otherwise, the user is prompted to authorize the pub client.
96 Future<Client> _getClient(SystemCache cache) { 99 Client _getClient(SystemCache cache) {
97 return _loadCredentials(cache).then((credentials) { 100 var credentials = _loadCredentials(cache);
98 if (credentials == null) return _authorize(); 101 if (credentials == null) return _authorize();
99 return new Client(_identifier, _secret, credentials, 102 var client = new Client(_identifier, _secret, credentials,
100 httpClient: curlClient); 103 httpClient: curlClient);
101 }).then((client) { 104 _saveCredentials(cache, client.credentials);
102 return _saveCredentials(cache, client.credentials).then((_) => client); 105 return client;
103 });
104 } 106 }
105 107
106 /// Loads the user's OAuth2 credentials from the in-memory cache or the 108 /// Loads the user's OAuth2 credentials from the in-memory cache or the
107 /// filesystem if possible. If the credentials can't be loaded for any reason, 109 /// filesystem if possible. If the credentials can't be loaded for any reason,
108 /// the returned [Future] will complete to null. 110 /// the returned [Future] will complete to null.
109 Future<Credentials> _loadCredentials(SystemCache cache) { 111 Credentials _loadCredentials(SystemCache cache) {
110 log.fine('Loading OAuth2 credentials.'); 112 log.fine('Loading OAuth2 credentials.');
111 113
112 if (_credentials != null) { 114 try {
113 log.fine('Using already-loaded credentials.'); 115 if (_credentials != null) return _credentials;
nweiz 2013/02/01 02:05:55 Why no more logging?
Bob Nystrom 2013/02/01 23:17:21 Two reasons: 1. The logging is most helpful when
114 return new Future.immediate(_credentials);
115 }
116 116
117 var path = _credentialsFile(cache); 117 var path = _credentialsFile(cache);
118 return fileExists(path).then((credentialsExist) { 118 var credentialsExist = fileExists(path);
119 if (!credentialsExist) { 119 if (!credentialsExist) return;
nweiz 2013/02/01 02:05:55 Style nit: merge this and the previous line.
Bob Nystrom 2013/02/01 23:17:21 Done.
120 log.fine('No credentials found at $path.'); 120
121 return; 121 var credentials = new Credentials.fromJson(
122 readTextFile(_credentialsFile(cache)));
nweiz 2013/02/01 02:05:55 "_credentialsFile(cache)" -> "path"
Bob Nystrom 2013/02/01 23:17:21 Done.
123 if (credentials.isExpired && !credentials.canRefresh) {
124 log.error("Pub's authorization to upload packages has expired and "
125 "can't be automatically refreshed.");
126 return null; // null means re-authorize.
122 } 127 }
123 128
124 return readTextFile(_credentialsFile(cache)).then((credentialsJson) { 129 return credentials;
125 var credentials = new Credentials.fromJson(credentialsJson); 130 } catch (e) {
126 if (credentials.isExpired && !credentials.canRefresh) {
127 log.error("Pub's authorization to upload packages has expired and "
128 "can't be automatically refreshed.");
129 return null; // null means re-authorize
130 }
131
132 return credentials;
133 });
134 }).catchError((e) {
135 log.error('Warning: could not load the saved OAuth2 credentials: $e\n' 131 log.error('Warning: could not load the saved OAuth2 credentials: $e\n'
136 'Obtaining new credentials...'); 132 'Obtaining new credentials...');
137 return null; // null means re-authorize 133 return null; // null means re-authorize.
138 }); 134 }
139 } 135 }
140 136
141 /// Save the user's OAuth2 credentials to the in-memory cache and the 137 /// Save the user's OAuth2 credentials to the in-memory cache and the
142 /// filesystem. 138 /// filesystem.
143 Future _saveCredentials(SystemCache cache, Credentials credentials) { 139 void _saveCredentials(SystemCache cache, Credentials credentials) {
144 log.fine('Saving OAuth2 credentials.'); 140 log.fine('Saving OAuth2 credentials.');
145 _credentials = credentials; 141 _credentials = credentials;
146 var path = _credentialsFile(cache); 142 var path = _credentialsFile(cache);
147 return ensureDir(dirname(path)).then((_) => 143 ensureDir(dirname(path));
148 writeTextFile(path, credentials.toJson(), dontLogContents: true)); 144 writeTextFile(path, credentials.toJson(), dontLogContents: true);
149 } 145 }
150 146
151 /// The path to the file in which the user's OAuth2 credentials are stored. 147 /// The path to the file in which the user's OAuth2 credentials are stored.
152 String _credentialsFile(SystemCache cache) => 148 String _credentialsFile(SystemCache cache) =>
153 join(cache.rootDir, 'credentials.json'); 149 join(cache.rootDir, 'credentials.json');
154 150
155 /// Gets the user to authorize pub as a client of pub.dartlang.org via oauth2. 151 /// Gets the user to authorize pub as a client of pub.dartlang.org via oauth2.
156 /// Returns a Future that will complete to a fully-authorized [Client]. 152 /// Returns a Future that will complete to a fully-authorized [Client].
157 Future<Client> _authorize() { 153 Future<Client> _authorize() {
158 // Allow the tests to inject their own token endpoint URL. 154 // Allow the tests to inject their own token endpoint URL.
(...skipping 11 matching lines...) Expand all
170 tokenEndpoint, 166 tokenEndpoint,
171 httpClient: curlClient); 167 httpClient: curlClient);
172 168
173 // Spin up a one-shot HTTP server to receive the authorization code from the 169 // Spin up a one-shot HTTP server to receive the authorization code from the
174 // Google OAuth2 server via redirect. This server will close itself as soon as 170 // Google OAuth2 server via redirect. This server will close itself as soon as
175 // the code is received. 171 // the code is received.
176 var completer = new Completer(); 172 var completer = new Completer();
177 var server = new HttpServer(); 173 var server = new HttpServer();
178 server.addRequestHandler((request) => request.path == "/", 174 server.addRequestHandler((request) => request.path == "/",
179 (request, response) { 175 (request, response) {
180 chainToCompleter(new Future.immediate(null).then((_) { 176 chainToCompleter(defer(() {
181 log.message('Authorization received, processing...'); 177 log.message('Authorization received, processing...');
182 var queryString = request.queryString; 178 var queryString = request.queryString;
183 if (queryString == null) queryString = ''; 179 if (queryString == null) queryString = '';
184 response.statusCode = 302; 180 response.statusCode = 302;
185 response.headers.set('location', 'http://pub.dartlang.org/authorized'); 181 response.headers.set('location', 'http://pub.dartlang.org/authorized');
186 response.outputStream.close(); 182 response.outputStream.close();
187 return grant.handleAuthorizationResponse(queryToMap(queryString)); 183 return grant.handleAuthorizationResponse(queryToMap(queryString));
188 }).then((client) { 184 }).then((client) {
189 server.close(); 185 server.close();
190 return client; 186 return client;
191 }), completer); 187 }), completer);
192 }); 188 });
193 server.listen('127.0.0.1', 0); 189 server.listen('127.0.0.1', 0);
194 190
195 var authUrl = grant.getAuthorizationUrl( 191 var authUrl = grant.getAuthorizationUrl(
196 Uri.parse('http://localhost:${server.port}'), scopes: _scopes); 192 Uri.parse('http://localhost:${server.port}'), scopes: _scopes);
197 193
198 log.message( 194 log.message(
199 'Pub needs your authorization to upload packages on your behalf.\n' 195 'Pub needs your authorization to upload packages on your behalf.\n'
200 'In a web browser, go to $authUrl\n' 196 'In a web browser, go to $authUrl\n'
201 'Then click "Allow access".\n\n' 197 'Then click "Allow access".\n\n'
202 'Waiting for your authorization...'); 198 'Waiting for your authorization...');
203 199
204 return completer.future.then((client) { 200 return completer.future.then((client) {
205 log.message('Successfully authorized.\n'); 201 log.message('Successfully authorized.\n');
206 return client; 202 return client;
207 }); 203 });
208 } 204 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698