OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #if !defined(__has_feature) || !__has_feature(objc_arc) | 5 #if !defined(__has_feature) || !__has_feature(objc_arc) |
6 #error "This file requires ARC support." | 6 #error "This file requires ARC support." |
7 #endif | 7 #endif |
8 | 8 |
9 #import "remoting/client/ios/facade/remoting_service.h" | 9 #import "remoting/client/ios/facade/remoting_service.h" |
10 | 10 |
11 #import <Foundation/Foundation.h> | 11 #import <Foundation/Foundation.h> |
| 12 #import <Security/Security.h> |
12 | 13 |
13 #import "base/mac/bind_objc_block.h" | 14 #import "base/mac/bind_objc_block.h" |
| 15 #import "remoting/client/ios/domain/host_info.h" |
| 16 #import "remoting/client/ios/domain/user_info.h" |
| 17 #import "remoting/client/ios/facade/host_info.h" |
| 18 #import "remoting/client/ios/facade/host_list_fetcher.h" |
| 19 #import "remoting/client/ios/facade/ios_client_runtime_delegate.h" |
| 20 #import "remoting/client/ios/facade/remoting_authentication.h" |
| 21 #import "remoting/client/ios/facade/remoting_service.h" |
| 22 #import "remoting/client/ios/keychain_wrapper.h" |
14 | 23 |
15 #include "base/logging.h" | 24 #include "base/logging.h" |
16 #include "base/strings/sys_string_conversions.h" | 25 #include "base/strings/sys_string_conversions.h" |
17 #include "net/url_request/url_request_context_getter.h" | 26 #include "net/url_request/url_request_context_getter.h" |
18 #include "remoting/base/oauth_token_getter.h" | 27 #include "remoting/base/oauth_token_getter.h" |
19 #include "remoting/base/oauth_token_getter_impl.h" | 28 #include "remoting/base/oauth_token_getter_impl.h" |
20 #include "remoting/client/ios/facade/host_info.h" | |
21 #include "remoting/client/ios/facade/host_list_fetcher.h" | |
22 #include "remoting/client/ios/facade/ios_client_runtime_delegate.h" | |
23 | 29 |
24 const char kOauthRedirectUrl[] = | 30 static NSString* const kCRDAuthenticatedUserEmailKey = |
25 "https://chromoting-oauth.talkgadget." | 31 @"kCRDAuthenticatedUserEmailKey"; |
26 "google.com/talkgadget/oauth/chrome-remote-desktop/dev"; | |
27 | 32 |
28 std::unique_ptr<remoting::OAuthTokenGetter> | 33 NSString* const kHostsDidUpdate = @"kHostsDidUpdate"; |
29 CreateOAuthTokenGetterWithAuthorizationCode( | |
30 const std::string& auth_code, | |
31 const remoting::OAuthTokenGetter::CredentialsUpdatedCallback& | |
32 on_credentials_update) { | |
33 std::unique_ptr<remoting::OAuthTokenGetter::OAuthIntermediateCredentials> | |
34 oauth_credentials( | |
35 new remoting::OAuthTokenGetter::OAuthIntermediateCredentials( | |
36 auth_code, /*is_service_account=*/false)); | |
37 oauth_credentials->oauth_redirect_uri = kOauthRedirectUrl; | |
38 | 34 |
39 std::unique_ptr<remoting::OAuthTokenGetter> oauth_tokenGetter( | 35 NSString* const kUserDidUpdate = @"kUserDidUpdate"; |
40 new remoting::OAuthTokenGetterImpl( | 36 NSString* const kUserInfo = @"kUserInfo"; |
41 std::move(oauth_credentials), on_credentials_update, | |
42 [[RemotingService SharedInstance] runtime]->url_requester(), | |
43 /*auto_refresh=*/true)); | |
44 return oauth_tokenGetter; | |
45 } | |
46 | 37 |
47 std::unique_ptr<remoting::OAuthTokenGetter> CreateOAuthTokenWithRefreshToken( | 38 @interface RemotingService ()<RemotingAuthenticationDelegate> { |
48 const std::string& refresh_token, | |
49 const std::string& email) { | |
50 std::unique_ptr<remoting::OAuthTokenGetter::OAuthAuthorizationCredentials> | |
51 oauth_credentials( | |
52 new remoting::OAuthTokenGetter::OAuthAuthorizationCredentials( | |
53 email, refresh_token, /*is_service_account=*/false)); | |
54 | |
55 std::unique_ptr<remoting::OAuthTokenGetter> oauth_tokenGetter( | |
56 new remoting::OAuthTokenGetterImpl( | |
57 std::move(oauth_credentials), | |
58 [[RemotingService SharedInstance] runtime]->url_requester(), | |
59 /*auto_refresh=*/true)); | |
60 return oauth_tokenGetter; | |
61 } | |
62 | |
63 @interface RemotingService () { | |
64 std::unique_ptr<remoting::OAuthTokenGetter> _tokenGetter; | 39 std::unique_ptr<remoting::OAuthTokenGetter> _tokenGetter; |
65 UserInfo* _user; | |
66 NSArray<HostInfo*>* _hosts; | |
67 id<RemotingAuthenticationDelegate> _authDelegate; | |
68 id<RemotingHostListDelegate> _hostListDelegate; | |
69 remoting::HostListFetcher* _hostListFetcher; | 40 remoting::HostListFetcher* _hostListFetcher; |
70 remoting::IosClientRuntimeDelegate* _clientRuntimeDelegate; | 41 remoting::IosClientRuntimeDelegate* _clientRuntimeDelegate; |
71 } | 42 } |
72 | |
73 @end | 43 @end |
74 | 44 |
75 // | |
76 // RemodingService will act as the facade to the C++ layer that has not been | |
77 // implemented/integrated yet. | |
78 // TODO(nicholss): Implement/Integrate this class. At the moment it is being | |
79 // used to generate fake data to implement the UI of the app. | |
80 // Update: Half implemented now. User is still fake, but now real hosts lists. | |
81 // | |
82 @implementation RemotingService | 45 @implementation RemotingService |
83 | 46 |
| 47 @synthesize authentication = _authentication; |
| 48 @synthesize hosts = _hosts; |
| 49 |
84 // RemotingService is a singleton. | 50 // RemotingService is a singleton. |
85 + (RemotingService*)SharedInstance { | 51 + (RemotingService*)SharedInstance { |
86 static RemotingService* sharedInstance = nil; | 52 static RemotingService* sharedInstance = nil; |
87 static dispatch_once_t guard; | 53 static dispatch_once_t guard; |
88 dispatch_once(&guard, ^{ | 54 dispatch_once(&guard, ^{ |
89 sharedInstance = [[RemotingService alloc] init]; | 55 sharedInstance = [[RemotingService alloc] init]; |
90 }); | 56 }); |
91 return sharedInstance; | 57 return sharedInstance; |
92 } | 58 } |
93 | 59 |
94 - (instancetype)init { | 60 - (instancetype)init { |
95 self = [super init]; | 61 self = [super init]; |
96 if (self) { | 62 if (self) { |
97 _user = nil; | 63 _authentication = [[RemotingAuthentication alloc] init]; |
| 64 _authentication.delegate = self; |
98 _hosts = nil; | 65 _hosts = nil; |
99 _hostListFetcher = new remoting::HostListFetcher( | 66 _hostListFetcher = nil; |
100 remoting::ChromotingClientRuntime::GetInstance()->url_requester()); | |
101 // TODO(nicholss): This might need a pointer back to the service. | 67 // TODO(nicholss): This might need a pointer back to the service. |
102 _clientRuntimeDelegate = | 68 _clientRuntimeDelegate = |
103 new remoting::IosClientRuntimeDelegate(); | 69 new remoting::IosClientRuntimeDelegate(); |
104 [self runtime]->SetDelegate(_clientRuntimeDelegate); | 70 [self runtime]->SetDelegate(_clientRuntimeDelegate); |
105 } | 71 } |
106 return self; | 72 return self; |
107 } | 73 } |
108 | 74 |
109 #pragma mark - RemotingService Implementation | 75 #pragma mark - RemotingService Implementation |
110 | 76 |
111 // TODO(nicholss): isAuthenticated needs to just kick off a request to | 77 - (void)startHostListFetchWith:(NSString*)accessToken { |
112 // authenticate a user. and more than one controller might want to be a delegate | 78 if (!_hostListFetcher) { |
113 // for this info so need to change this to be more of the registration types. | 79 _hostListFetcher = new remoting::HostListFetcher( |
114 // The remoting_service might also want to be registered for authentication | 80 remoting::ChromotingClientRuntime::GetInstance()->url_requester()); |
115 // changes and it can update it's cache as it needs. | |
116 | |
117 - (void)setAuthenticationDelegate:(id<RemotingAuthenticationDelegate>)delegate { | |
118 _authDelegate = delegate; | |
119 if (_authDelegate) { | |
120 [_authDelegate nowAuthenticated:[self isAuthenticated]]; | |
121 } | 81 } |
122 if (!_user && _tokenGetter) { | 82 _hostListFetcher->RetrieveHostlist( |
123 _tokenGetter->CallWithToken(base::BindBlockArc( | 83 base::SysNSStringToUTF8(accessToken), |
124 ^(remoting::OAuthTokenGetter::Status status, | 84 base::BindBlockArc(^(const std::vector<remoting::HostInfo>& hostlist) { |
125 const std::string& user_email, const std::string& access_token) { | 85 NSMutableArray<HostInfo*>* hosts = |
126 if (status == remoting::OAuthTokenGetter::Status::SUCCESS) { | 86 [NSMutableArray arrayWithCapacity:hostlist.size()]; |
127 _user = [[UserInfo alloc] init]; | 87 std::string status; |
128 _user.userEmail = | 88 for (const remoting::HostInfo& host_info : hostlist) { |
129 [NSString stringWithCString:user_email.c_str() | 89 remoting::HostStatus host_status = host_info.status; |
130 encoding:[NSString defaultCStringEncoding]]; | 90 switch (host_status) { |
131 } else { | 91 case remoting::kHostStatusOnline: |
132 _user = nil; | 92 status = "ONLINE"; |
| 93 break; |
| 94 case remoting::kHostStatusOffline: |
| 95 status = "OFFLINE"; |
| 96 break; |
| 97 default: |
| 98 NOTREACHED(); |
133 } | 99 } |
134 if (_authDelegate) { | 100 // TODO(nicholss): Not yet integrated: createdTime, hostVersion, |
135 [_authDelegate nowAuthenticated:[self isAuthenticated]]; | 101 // kind, offlineReason. Add them as the app will need this info. |
136 } | 102 HostInfo* host = [[HostInfo alloc] init]; |
137 })); | 103 host.hostId = |
138 } | 104 [NSString stringWithCString:host_info.host_id.c_str() |
| 105 encoding:[NSString defaultCStringEncoding]]; |
| 106 host.hostName = |
| 107 [NSString stringWithCString:host_info.host_name.c_str() |
| 108 encoding:[NSString defaultCStringEncoding]]; |
| 109 host.jabberId = |
| 110 [NSString stringWithCString:host_info.host_jid.c_str() |
| 111 encoding:[NSString defaultCStringEncoding]]; |
| 112 host.publicKey = |
| 113 [NSString stringWithCString:host_info.public_key.c_str() |
| 114 encoding:[NSString defaultCStringEncoding]]; |
| 115 host.status = |
| 116 [NSString stringWithCString:status.c_str() |
| 117 encoding:[NSString defaultCStringEncoding]]; |
| 118 [hosts addObject:host]; |
| 119 } |
| 120 _hosts = hosts; |
| 121 [self hostListUpdated]; |
| 122 })); |
139 } | 123 } |
140 | 124 |
141 - (BOOL)isAuthenticated { | 125 #pragma mark - Notifications |
142 if (_user) { | 126 |
143 return YES; | 127 - (void)hostListUpdated { |
144 } | 128 [[NSNotificationCenter defaultCenter] postNotificationName:kHostsDidUpdate |
145 return NO; | 129 object:self |
| 130 userInfo:nil]; |
146 } | 131 } |
147 | 132 |
148 - (void)startHostListFetchWith:(NSString*)accessToken { | 133 #pragma mark - RemotingAuthenticationDelegate |
149 NSLog(@"startHostListFetchWith : %@ %@", accessToken, _authDelegate); | |
150 if (_authDelegate) { | |
151 [_authDelegate nowAuthenticated:YES]; | |
152 | 134 |
153 _hostListFetcher->RetrieveHostlist( | 135 - (void)userDidUpdate:(UserInfo*)user { |
154 base::SysNSStringToUTF8(accessToken), | 136 NSDictionary* userInfo = nil; |
155 base::BindBlockArc(^(const std::vector<remoting::HostInfo>& hostlist) { | 137 if (user) { |
156 NSMutableArray<HostInfo*>* hosts = | 138 userInfo = [NSDictionary dictionaryWithObject:user forKey:kUserInfo]; |
157 [NSMutableArray arrayWithCapacity:hostlist.size()]; | 139 } else { |
158 std::string status; | 140 _hosts = nil; |
159 for (const remoting::HostInfo& host_info : hostlist) { | 141 [self hostListUpdated]; |
160 remoting::HostStatus host_status = host_info.status; | |
161 switch (host_status) { | |
162 case remoting::kHostStatusOnline: | |
163 status = "ONLINE"; | |
164 break; | |
165 case remoting::kHostStatusOffline: | |
166 status = "OFFLINE"; | |
167 break; | |
168 default: | |
169 NOTREACHED(); | |
170 } | |
171 // TODO(nicholss): Not yet integrated: createdTime, hostVersion, | |
172 // kind, offlineReason. Add them as the app will need this info. | |
173 HostInfo* host = [[HostInfo alloc] init]; | |
174 host.hostId = | |
175 [NSString stringWithCString:host_info.host_id.c_str() | |
176 encoding:[NSString defaultCStringEncoding]]; | |
177 host.hostName = | |
178 [NSString stringWithCString:host_info.host_name.c_str() | |
179 encoding:[NSString defaultCStringEncoding]]; | |
180 host.jabberId = | |
181 [NSString stringWithCString:host_info.host_jid.c_str() | |
182 encoding:[NSString defaultCStringEncoding]]; | |
183 host.publicKey = | |
184 [NSString stringWithCString:host_info.public_key.c_str() | |
185 encoding:[NSString defaultCStringEncoding]]; | |
186 host.status = | |
187 [NSString stringWithCString:status.c_str() | |
188 encoding:[NSString defaultCStringEncoding]]; | |
189 [hosts addObject:host]; | |
190 } | |
191 _hosts = hosts; | |
192 [_hostListDelegate hostListUpdated]; | |
193 })); | |
194 } | 142 } |
| 143 [[NSNotificationCenter defaultCenter] postNotificationName:kUserDidUpdate |
| 144 object:self |
| 145 userInfo:userInfo]; |
195 } | 146 } |
196 | 147 |
197 - (void)authenticateWithAuthorizationCode:(NSString*)authorizationCode { | 148 #pragma mark - Properties |
198 _tokenGetter = CreateOAuthTokenGetterWithAuthorizationCode( | |
199 std::string(base::SysNSStringToUTF8(authorizationCode)), | |
200 base::BindBlockArc( | |
201 ^(const std::string& user_email, const std::string& refresh_token) { | |
202 // TODO(nicholss): Do something with these new creds. | |
203 VLOG(1) << "New Creds: " << user_email << " " << refresh_token; | |
204 })); | |
205 } | |
206 | 149 |
207 - (void)authenticateWithRefreshToken:(NSString*)refreshToken | 150 - (NSArray<HostInfo*>*)hosts { |
208 email:(NSString*)email { | 151 if ([_authentication.user isAuthenticated]) { |
209 _tokenGetter = CreateOAuthTokenWithRefreshToken( | 152 return _hosts; |
210 std::string(base::SysNSStringToUTF8(refreshToken)), | |
211 base::SysNSStringToUTF8(email)); | |
212 } | |
213 | |
214 - (UserInfo*)getUser { | |
215 if (![self isAuthenticated]) { | |
216 return nil; | |
217 } | 153 } |
218 | 154 return nil; |
219 NSMutableString* json = [[NSMutableString alloc] init]; | |
220 [json appendString:@"{"]; | |
221 [json appendString:@"\"userId\":\"AABBCC123\","]; | |
222 [json appendString:@"\"userFullName\":\"John Smith\","]; | |
223 [json appendString:@"\"userEmail\":\"john@example.com\","]; | |
224 [json appendString:@"}"]; | |
225 | |
226 NSMutableData* data = [NSMutableData | |
227 dataWithData:[[json copy] dataUsingEncoding:NSUTF8StringEncoding]]; | |
228 | |
229 UserInfo* user = [UserInfo parseListFromJSON:data]; | |
230 return user; | |
231 } | |
232 | |
233 - (void)setHostListDelegate:(id<RemotingHostListDelegate>)delegate { | |
234 bool attemptUpdate = (_hostListDelegate != delegate); | |
235 _hostListDelegate = delegate; | |
236 if (attemptUpdate && _hostListDelegate && _tokenGetter) { | |
237 // TODO(nicholss): It might be cleaner to set the delegate and then have | |
238 // them ask to refresh the host list rather than start this get hosts call. | |
239 _tokenGetter->CallWithToken(base::BindBlockArc( | |
240 ^(remoting::OAuthTokenGetter::Status status, | |
241 const std::string& user_email, const std::string& access_token) { | |
242 NSString* accessToken = | |
243 [NSString stringWithCString:access_token.c_str() | |
244 encoding:[NSString defaultCStringEncoding]]; | |
245 [self startHostListFetchWith:accessToken]; | |
246 })); | |
247 } | |
248 } | |
249 | |
250 - (NSArray<HostInfo*>*)getHosts { | |
251 if (![self isAuthenticated]) { | |
252 return nil; | |
253 } | |
254 return _hosts; | |
255 } | 155 } |
256 | 156 |
257 - (remoting::ChromotingClientRuntime*)runtime { | 157 - (remoting::ChromotingClientRuntime*)runtime { |
258 return remoting::ChromotingClientRuntime::GetInstance(); | 158 return remoting::ChromotingClientRuntime::GetInstance(); |
259 } | 159 } |
260 | 160 |
261 - (void)callbackWithAccessToken: | 161 #pragma mark - Implementation |
262 (const remoting::OAuthTokenGetter::TokenCallback&)onAccessToken { | 162 |
263 if (_tokenGetter) { | 163 - (void)requestHostListFetch { |
264 _tokenGetter->CallWithToken(onAccessToken); | 164 [_authentication |
265 } | 165 callbackWithAccessToken:base::BindBlockArc(^( |
| 166 remoting::OAuthTokenGetter::Status status, |
| 167 const std::string& user_email, |
| 168 const std::string& access_token) { |
| 169 NSString* accessToken = |
| 170 [NSString stringWithCString:access_token.c_str() |
| 171 encoding:[NSString defaultCStringEncoding]]; |
| 172 [self startHostListFetchWith:accessToken]; |
| 173 })]; |
266 } | 174 } |
267 | 175 |
268 @end | 176 @end |
OLD | NEW |