source: trunk/src/org/expeditee/auth/account/Create.java@ 1504

Last change on this file since 1504 was 1504, checked in by bnemhaus, 4 years ago

Revised implementation of authenticated Expeditee mail. Motivated by bugs relating to messages not being marked as read and incorrect counting of new messages for users, the Expeditee mail system has been rewritten. The new code not only does not exhibit the previous bugs but is also better engineered. Whilst the MailBay is static (which is in line with the MessageBay), the Mail class is no longer static and must be initialised for each user as they log in.

File size: 13.5 KB
Line 
1package org.expeditee.auth.account;
2
3import java.io.File;
4import java.io.FileWriter;
5import java.io.IOException;
6import java.nio.file.Path;
7import java.nio.file.Paths;
8import java.security.KeyPair;
9import java.security.KeyPairGenerator;
10import java.security.KeyStoreException;
11import java.security.NoSuchAlgorithmException;
12import java.security.SecureRandom;
13import java.security.cert.CertificateException;
14import java.sql.SQLException;
15import java.util.Base64;
16import java.util.Collection;
17import java.util.HashMap;
18import java.util.Map;
19import java.util.Random;
20import java.util.function.Consumer;
21
22import javax.crypto.SecretKey;
23import javax.crypto.spec.SecretKeySpec;
24
25import org.apollo.io.AudioPathManager;
26import org.expeditee.agents.ExistingFramesetException;
27import org.expeditee.agents.InvalidFramesetNameException;
28import org.expeditee.auth.Actions;
29import org.expeditee.auth.AuthenticatorBrowser;
30import org.expeditee.auth.tags.AuthenticationTag;
31import org.expeditee.core.Colour;
32import org.expeditee.encryption.CryptographyConstants;
33import org.expeditee.gui.DisplayController;
34import org.expeditee.gui.Frame;
35import org.expeditee.gui.FrameIO;
36import org.expeditee.gui.MessageBay;
37import org.expeditee.gui.MessageBay.Progress;
38import org.expeditee.gui.management.ProfileManager;
39import org.expeditee.io.ExpReader;
40import org.expeditee.items.Item;
41import org.expeditee.items.PermissionTriple;
42import org.expeditee.items.Text;
43import org.expeditee.items.UserAppliedPermission;
44import org.expeditee.setting.GenericSetting;
45import org.expeditee.setting.Setting;
46import org.expeditee.setting.TextSetting;
47import org.expeditee.settings.UserSettings;
48import org.expeditee.settings.identity.secrets.KeyList;
49
50public class Create implements CryptographyConstants {
51
52 /**
53 * Create a user account using the specified information in userdata. Creates and stores user keys.
54 * @param userdata Should contain username, password and email.
55 */
56 public static CreateResult createAccount(Map<AuthenticationTag, String> userdata) {
57 // Track progress
58 String message = "Creating new user account...";
59 int progress = 0;
60 int step = 16;
61
62 // Extract user details
63 String username = userdata.get(AuthenticationTag.Username);
64 String password = userdata.get(AuthenticationTag.Password);
65 String email = userdata.get(AuthenticationTag.Email);
66
67 Progress progressBar = MessageBay.displayProgress(message);
68 progress = progress(message + "Generating Keys.", progress, step, progressBar);
69
70 // Generate keys
71 // Personal key
72 String personalKey = generatePersonalKey(username, password);
73 if (personalKey == null) {
74 return CreateResult.ErrorSymmetricKey;
75 }
76 // Public and private keys
77 String[] keys = generateAsymmetricKeys();
78 if (keys == null) {
79 return CreateResult.ErrorAsymmetricKeys;
80 }
81 String privateKey = keys[0];
82 String publicKey = keys[1];
83
84 progress = progress(message + "Creating Profile Frameset.", progress, step, progressBar);
85
86 // Update in memory settings
87 ProfileManager.ensureDefaultProfile();
88 System.setProperty("user.name", username);
89 UserSettings.UserName.set(username);
90 //UserSettings.ProfileName.set(username);
91 UserSettings.ProfileName.set(FrameIO.ConvertToValidFramesetName(username));
92 UserSettings.setupDefaultFolders();
93
94 Frame profile;
95 try {
96 profile = createNewProfile(username, email, personalKey, privateKey, publicKey);
97 } catch (InvalidFramesetNameException | ExistingFramesetException e) {
98 return CreateResult.ErrorNewProfile;
99 }
100
101 if (AuthenticatorBrowser.CREDENTIALS_FRAME == -1) {
102 return CreateResult.ErrorCredentialsFrame;
103 }
104
105 progress = progress(message + "Establishing user credentials.", progress, step, progressBar);
106
107 // Create credentials
108 boolean success = setupCredentialsFrameAndINFs(username, profile);
109 if (!success) {
110 return CreateResult.ErrorIODuringCredentialsFrameSetup;
111 }
112
113 progress = progress(message + "Creating Individual Space.", progress, step, progressBar);
114
115 // Copy private resources to personal area
116 Path personalResources = createPersonalArea(username);
117
118 progress = progress(message + "Creating Space For Dead Drops.", progress, step, progressBar);
119
120 createDeaddropsArea(personalResources);
121
122 System.err.println("**** Hardwired call in Apollo's AuthioPathManager");
123 AudioPathManager.activateAndScanAudioDirs(); // ****
124
125 progress = progress(message + "Done.", progress, step, progressBar);
126
127 return CreateResult.SuccessCreateAccount;
128 }
129
130 public enum CreateResult {
131 SuccessCreateAccount ("Account created."),
132 SuccessAlternativeAccount ("Alternative access to account established."),
133 ErrorSymmetricKey ("An error occured while trying to generate your personal key."),
134 ErrorAsymmetricKeys ("An error occured while trying to generate asymmetric keys."),
135 ErrorNewProfile ("An error occured while creating the profile frames."),
136 ErrorCredentialsFrame ("Unable to establish credentials frame for new profile frame."),
137 ErrorIODuringCredentialsFrameSetup ("An error occured during the setup of the new users credentials frame.");
138
139 private String message = null;
140
141 private CreateResult(String message) {
142 this.message = message;
143 }
144
145 public String toString() {
146 return message;
147 }
148 }
149
150 private static int progress(String message, int progress, int step, Progress progressBar) {
151 try {
152 progressBar.UpdateMessage(message, progress += step);
153 } catch (Exception e) {
154 e.printStackTrace();
155 }
156 DisplayController.refreshBayArea();
157 return progress;
158 }
159
160 private static void createDeaddropsArea(Path personalResources) {
161 File deadDropsDir = new File(personalResources.resolve("deaddrops").toAbsolutePath().toString());
162 deadDropsDir.mkdir();
163 }
164
165 private static Path createPersonalArea(String username) {
166 Path personalResources = UserSettings.PublicAndPrivateResources ? FrameIO.setupPersonalResources(username) : Paths.get(FrameIO.PARENT_FOLDER);
167
168 File contactsDir = new File(personalResources.resolve("contacts").toAbsolutePath().toString());
169 contactsDir.mkdir();
170 return personalResources;
171 }
172
173 private static boolean setupCredentialsFrameAndINFs(String username, Frame profile) {
174 try {
175 File credentialsDir = new File(profile.getFramesetPath() + username + "-credentials");
176 credentialsDir.mkdir();
177 // credentials.inf file.
178 String credentialsPath = credentialsDir.getAbsolutePath() + File.separator + "credentials.inf";
179 File credentialsFile = new File(credentialsPath);
180 credentialsFile.createNewFile();
181 FileWriter out = new FileWriter(credentialsFile);
182 out.write(AuthenticatorBrowser.CREDENTIALS_FRAME + ".exp");
183 out.flush();
184 out.close();
185 // migrate credentials frame
186 Frame credentialsFrame = FrameIO.LoadFrame(username + AuthenticatorBrowser.CREDENTIALS_FRAME);
187 Path destinationDirectory = Paths.get(credentialsDir.getAbsolutePath());
188 Path destinationFile = destinationDirectory.resolve(AuthenticatorBrowser.CREDENTIALS_FRAME + ExpReader.EXTENTION);
189 FrameIO.migrateFrame(credentialsFrame, destinationFile);
190 // pwcolleagues.inf file.
191 File pwColleaguesFile = Paths.get(FrameIO.PROFILE_PATH).resolve(username).resolve("pwcolleagues.inf").toFile();
192 pwColleaguesFile.createNewFile();
193 out = new FileWriter(pwColleaguesFile);
194 out.write(AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME + ".exp");
195 out.flush();
196 out.close();
197 return true;
198 } catch (IOException e) {
199 return false;
200 }
201 }
202
203 private static Frame createNewProfile(String username, String email, String personalKey, String privateKey,
204 String publicKey) throws InvalidFramesetNameException, ExistingFramesetException {
205 // Establish the initial settings for the created user.
206 Map<String, Setting> initialSettings = new HashMap<String, Setting>();
207 initialSettings.put("settings.identity.secrets.PersonalKey", constructTextSetting("The AES key used to secure your profile frame - do not share with anyone!", "PersonalKey", personalKey));
208 initialSettings.put("settings.identity.secrets.PrivateKey", constructTextSetting("The RSA key used to decrypt things encrypted with your RSA public key - do not share with anyone!", "PrivateKey", privateKey));
209 initialSettings.put("settings.identity.PublicKey", constructTextSetting("The RSA key used to decrypt things encrypted with your RSA public key.", "PublicKey", publicKey));
210 initialSettings.put("settings.identity.Email", constructGenericSetting(String.class, "Your public-facing email address.", "Email", email, username));
211 initialSettings.put("settings.UserSettings.UserName", constructGenericSetting(String.class, "UserName", "UserName", username, username));
212 initialSettings.put("settings.UserSettings.ProfileName", constructGenericSetting(String.class, "ProfileName", "ProfileName", username, username));
213 initialSettings.put("settings.UserSettings.HomeFrame", constructGenericSetting(String.class, "The home frame", "HomeFrame", username + 1, username));
214 //String resourcesPrivateUserPath = FrameIO.PARENT_FOLDER + "resources-" + UserSettings.UserName.get() + File.separator;
215 //FolderSettings.FrameDirs.addAbsoluteDir(resourcesPrivateUserPath + "messages" + File.separator);
216 //initialSettings.put("org.expeditee.gui.folders.FolderSettings.FrameDirs", FolderSettings.FrameDirs);
217 //initialSettings.put("org.expeditee.gui.folders.FolderSettings.ImageDirs", FolderSettings.ImageDirs);
218 //initialSettings.put("org.expeditee.gui.folders.FolderSettings.AudioDirs", FolderSettings.AudioDirs);
219
220 // Record the credentials frame number and password colleagues frame
221 Map<String, Consumer<Frame>> notifiers = new HashMap<String, Consumer<Frame>>();
222 notifiers.put("settings.identity", frame -> {
223 AuthenticatorBrowser.CREDENTIALS_FRAME = frame.getNumber();
224 frame.addToData("MultiuserCredentials");
225 Collection<Text> textItems = frame.getTextItems();
226 for (Text t: textItems) {
227 if (t.getText().equals("Secrets")) {
228 t.setPermission(new PermissionTriple(UserAppliedPermission.followLinks, UserAppliedPermission.denied, UserAppliedPermission.denied));
229 break;
230 }
231 }
232 });
233 notifiers.put("settings.identity.passwordrecovery", frame -> {
234 frame.addToData("PasswordColleagues");
235 AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME = frame.getNumber();
236 });
237 notifiers.put("settings.identity.secrets", frame -> {
238 frame.addToData("SecretsFrame");
239 AuthenticatorBrowser.SECRETS_FRAME = frame.getNumber();
240 });
241
242 // Create users profile
243 Frame profile = FrameIO.CreateNewProfile(username, initialSettings, notifiers);
244 int lastNumber = FrameIO.getLastNumber(profile.getFramesetName());
245 for (int i = 1; i <= lastNumber; i++) {
246 Frame f = FrameIO.LoadFrame(profile.getFramesetName() + i);
247 Text titleItem = f.getTitleItem();
248 if (i == 1 && titleItem != null) {
249 titleItem.delete();
250 f.setBackgroundColor(new Colour(1, 1, 0.39f));
251 }
252 f.setOwner(username);
253 f.getAllItems().stream().forEach(item -> item.setOwner(username));
254 f.setChanged(true);
255 if (f.getNumber() != AuthenticatorBrowser.CREDENTIALS_FRAME &&
256 f.getNumber() != AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME) {
257 f.setEncryptionLabel(AuthenticatorBrowser.PROFILEENCRYPTIONLABEL);
258 }
259 Collection<Item> secretsLink = Actions.getByContent(f, "Secrets");
260 Collection<Item> publicKeyItem = Actions.getByContent(f, "PublicKey");
261 if (!secretsLink.isEmpty() && !publicKeyItem.isEmpty()) {
262 //Then we are on credentials frame
263 f.addToData("MultiuserCredentials");
264 }
265 Text backupPersonalKey = KeyList.PersonalKey.get();
266 Text tempPersonalKey = KeyList.PersonalKey.generateText();
267 tempPersonalKey.setData(personalKey);
268 KeyList.PersonalKey.setSetting(tempPersonalKey);
269 FrameIO.SaveFrame(f);
270 KeyList.PersonalKey.setSetting(backupPersonalKey);
271 }
272 return profile;
273 }
274
275 private static String generatePersonalKey(String username, String password) {
276 try {
277 Random rand = new SecureRandom();
278 byte[] keyBytes = new byte[16];
279 rand.nextBytes(keyBytes);
280 SecretKey key = new SecretKeySpec(keyBytes, SymmetricAlgorithm);
281 AuthenticatorBrowser.getInstance().putKey(username, password, key);
282 String personalKey = Base64.getEncoder().encodeToString(key.getEncoded());
283 return personalKey;
284 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | ClassNotFoundException
285 | IOException | SQLException e) {
286 return null;
287 }
288 }
289
290 private static String[] generateAsymmetricKeys() {
291 try {
292 KeyPairGenerator keyGen = KeyPairGenerator.getInstance(AsymmetricAlgorithm);
293 keyGen.initialize(1024);
294 KeyPair keyPair = keyGen.generateKeyPair();
295 String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
296 String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
297 return new String[] { privateKey, publicKey };
298 } catch (NoSuchAlgorithmException e) {
299 return null;
300 }
301 }
302
303 private static TextSetting constructTextSetting(String tooltip, String text, String data) {
304 return new TextSetting(tooltip, text) {
305 @Override
306 public Text generateText() {
307 Text t = new Text(text);
308 t.setData(data);
309 return t;
310 }
311 };
312 }
313
314 private static <T> GenericSetting<T> constructGenericSetting(Class<T> type, String tooltip, String name, T value, String frameset) {
315 return new GenericSetting<T>(type, tooltip, name, value) {
316 @Override
317 public Text generateRepresentation(String name, String frameset) {
318 Text t = new Text(name + ": " + value);
319 return t;
320 }
321 };
322 }
323}
Note: See TracBrowser for help on using the repository browser.