OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "services/authentication/accounts_db_manager.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/strings/string_tokenizer.h" | |
11 #include "mojo/public/cpp/bindings/array.h" | |
12 #include "mojo/public/cpp/bindings/type_converter.h" | |
13 #include "mojo/services/files/interfaces/files.mojom.h" | |
14 | |
15 namespace authentication { | |
16 | |
17 const char* kAccountsDbFileName = "accounts_db.txt"; | |
18 | |
19 AccountsDbManager::AccountsDbManager() | |
20 : directory_(nullptr), contents_(nullptr) {} | |
21 | |
22 AccountsDbManager::AccountsDbManager(mojo::files::FilesPtr files) { | |
23 // TODO: Move to a file system with secure privileges as the accounts db needs | |
24 // to persist across multiple invocations and apps, and the temporary root | |
25 // solution here is just a short term path. | |
26 mojo::files::Error error = mojo::files::Error::INTERNAL; | |
27 files->OpenFileSystem(nullptr, GetProxy(&directory_), Capture(&error)); | |
viettrungluu
2015/12/18 20:59:49
I don't think you should block the thread (and eve
ukode
2016/01/06 23:52:59
Good point. Made the change.
| |
28 files.WaitForIncomingResponse(); | |
29 if (mojo::files::Error::OK != error) { | |
30 LOG(FATAL) << "Unable to initialize accounts DB"; | |
31 } | |
32 } | |
33 | |
34 AccountsDbManager::~AccountsDbManager() {} | |
35 | |
36 void AccountsDbManager::GetUpdatedDbContents( | |
37 const mojo::String& username, | |
38 const mojo::String& new_account_data, | |
39 const bool user_exists, | |
40 mojo::String& new_db_contents) { | |
41 if (username.is_null() || new_account_data.is_null()) { | |
42 return; | |
43 } | |
44 std::string buffer; | |
45 if (user_exists) { | |
46 // Account identified as new user, add a fresh record for this user in | |
47 // accounts DB in Append mode. | |
48 if (!AccountsDbManager::contents_.empty()) { | |
49 buffer += "\n"; | |
50 } | |
51 buffer += new_account_data; | |
52 } else { | |
53 // Existing user account, replace the credentials/tokens to newly | |
54 // generated auth tokens in-place. | |
55 base::StringTokenizer lines(AccountsDbManager::contents_, "\n"); | |
56 std::string user_record; | |
57 while (lines.GetNext()) { | |
58 user_record = lines.token(); | |
59 if (!new_db_contents.is_null() || !user_record.empty()) { | |
60 buffer += "\n"; | |
61 } | |
62 if (user_record.find(username) != std::string::npos) { | |
63 buffer += new_account_data; // replace with new auth data | |
64 } else { | |
65 buffer += user_record; | |
66 } | |
67 } | |
68 } | |
69 | |
70 new_db_contents = buffer; | |
71 return; | |
72 } | |
73 | |
74 bool AccountsDbManager::UpdateAccount(const mojo::String& username, | |
75 const mojo::String& new_account_data) { | |
76 if (username.is_null() || new_account_data.is_null()) { | |
77 return false; | |
78 } | |
79 mojo::String existing_user_data; | |
80 AccountsDbManager::GetAccountDataForUser(username, existing_user_data); | |
81 mojo::String new_db_contents; | |
82 GetUpdatedDbContents(username, new_account_data, existing_user_data.is_null(), | |
83 new_db_contents); | |
84 if (new_db_contents.is_null()) { | |
85 return false; | |
86 } | |
87 | |
88 // Open accounts db file | |
89 mojo::files::FilePtr file; | |
90 mojo::files::Error error = mojo::files::Error::INTERNAL; | |
91 bool write_failed = false; | |
92 if (existing_user_data.is_null()) { | |
93 // Append to existing file | |
94 directory_->OpenFile(kAccountsDbFileName, GetProxy(&file), | |
95 mojo::files::kOpenFlagWrite | | |
96 mojo::files::kOpenFlagCreate | | |
97 mojo::files::kOpenFlagAppend, | |
98 Capture(&error)); | |
viettrungluu
2015/12/18 20:59:49
In general, you shouldn't do synchronous I/O (i.e.
ukode
2016/01/06 23:52:59
Moved the opening and closing files to ctor and dt
| |
99 } else { | |
100 // Rewrite the file contents with updated info | |
101 directory_->OpenFile( | |
102 kAccountsDbFileName, GetProxy(&file), | |
103 mojo::files::kOpenFlagWrite | mojo::files::kOpenFlagCreate, | |
104 Capture(&error)); | |
105 } | |
106 directory_.WaitForIncomingResponse(); | |
107 if (mojo::files::Error::OK != error) { | |
108 write_failed = true; | |
109 } else { | |
110 // Write to it. | |
111 std::vector<uint8_t> bytes_to_write(new_db_contents.get().begin(), | |
112 new_db_contents.get().end()); | |
113 bytes_to_write.push_back('\0'); | |
114 error = mojo::files::Error::INTERNAL; | |
115 uint32_t num_bytes_written = 0; | |
116 file->Write(mojo::Array<uint8_t>::From(bytes_to_write), 0, | |
117 mojo::files::Whence::FROM_CURRENT, | |
118 Capture(&error, &num_bytes_written)); | |
119 file.WaitForIncomingResponse(); | |
120 | |
121 if (mojo::files::Error::OK != error) { | |
122 write_failed = true; | |
123 } | |
124 } | |
125 | |
126 // Close the accounts db file | |
127 error = mojo::files::Error::INTERNAL; | |
128 file->Close(Capture(&error)); | |
129 file.WaitForIncomingResponse(); | |
130 if (mojo::files::Error::OK != error) { | |
131 return false; | |
132 } | |
133 | |
134 if (write_failed) { | |
135 return false; | |
136 } | |
137 | |
138 // Update the existing contents with new data | |
139 AccountsDbManager::contents_.assign(new_db_contents.data(), | |
140 new_db_contents.size()); | |
141 return true; | |
142 } | |
143 | |
144 void AccountsDbManager::GetAccountDataForUser(const mojo::String& username, | |
145 mojo::String& user_data) { | |
146 if (username.is_null()) { | |
147 return; | |
148 } | |
149 | |
150 if (AccountsDbManager::contents_.empty()) { | |
151 mojo::Array<uint8_t> all_accounts_data( | |
152 AccountsDbManager::FetchAllAccounts()); | |
153 if (!all_accounts_data.size()) { | |
154 return; | |
155 } | |
156 } | |
157 | |
158 base::StringTokenizer lines(AccountsDbManager::contents_, "\n"); | |
159 std::string entry; | |
160 while (lines.GetNext()) { | |
161 entry = lines.token(); | |
162 if (entry.find(username) != std::string::npos) { | |
163 user_data.Swap(&entry); | |
164 return; | |
165 } | |
166 } | |
167 } | |
168 | |
169 mojo::Array<uint8_t> AccountsDbManager::FetchAllAccounts() { | |
170 const size_t kMaxReadSize = 1 * 1024 * 1024; | |
171 bool read_failed = false; | |
172 mojo::Array<uint8_t> bytes_read; | |
173 | |
174 // Open accounts db file | |
175 mojo::files::FilePtr file; | |
176 mojo::files::Error error = mojo::files::Error::INTERNAL; | |
177 directory_->OpenFile(kAccountsDbFileName, GetProxy(&file), | |
178 mojo::files::kOpenFlagRead, Capture(&error)); | |
179 directory_.WaitForIncomingResponse(); | |
180 if (mojo::files::Error::OK != error) { | |
181 read_failed = true; | |
182 } else { | |
183 // Read from it. | |
184 error = mojo::files::Error::INTERNAL; | |
185 file->Read(kMaxReadSize - 1, 0, mojo::files::Whence::FROM_START, | |
186 Capture(&error, &bytes_read)); | |
187 | |
188 file.WaitForIncomingResponse(); | |
189 if (mojo::files::Error::OK != error) { | |
190 read_failed = true; | |
191 } | |
192 } | |
193 | |
194 // Close the accounts db file | |
195 error = mojo::files::Error::INTERNAL; | |
196 file->Close(Capture(&error)); | |
197 file.WaitForIncomingResponse(); | |
198 if (mojo::files::Error::OK != error) { | |
199 return mojo::Array<uint8_t>(); | |
200 } | |
201 | |
202 if (read_failed) { | |
203 return mojo::Array<uint8_t>(); | |
204 } | |
205 | |
206 const std::vector<uint8_t> vec = bytes_read.storage(); | |
207 AccountsDbManager::contents_.assign((char*)vec.data(), vec.size()); | |
208 | |
209 return bytes_read.Pass(); | |
210 } | |
211 | |
212 } // namespace authentication | |
OLD | NEW |