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

Last change on this file since 1352 was 1352, checked in by bln4, 5 years ago

Refactor of account creation action.
New settings frame to record a users password recovery colleagues.
This new frame is unencrypted.

File size: 12.9 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.gui.DisplayController;
33import org.expeditee.gui.Frame;
34import org.expeditee.gui.FrameIO;
35import org.expeditee.gui.MessageBay;
36import org.expeditee.gui.MessageBay.Progress;
37import org.expeditee.io.ExpReader;
38import org.expeditee.items.Item;
39import org.expeditee.items.PermissionPair;
40import org.expeditee.items.Text;
41import org.expeditee.items.UserAppliedPermission;
42import org.expeditee.setting.GenericSetting;
43import org.expeditee.setting.Setting;
44import org.expeditee.setting.TextSetting;
45import org.expeditee.settings.UserSettings;
46import org.expeditee.settings.folders.FolderSettings;
47import org.expeditee.settings.identity.secrets.KeyList;
48import org.ngikm.cryptography.CryptographyConstants;
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 System.setProperty("user.name", username);
88 UserSettings.UserName.set(username);
89 UserSettings.ProfileName.set(username);
90 UserSettings.setupDefaultFolders();
91
92 Frame profile;
93 try {
94 profile = createNewProfile(username, email, personalKey, privateKey, publicKey);
95 } catch (InvalidFramesetNameException | ExistingFramesetException e) {
96 return CreateResult.ErrorNewProfile;
97 }
98
99 if (AuthenticatorBrowser.CREDENTIALS_FRAME == -1) {
100 return CreateResult.ErrorCredentialsFrame;
101 }
102
103 progress = progress(message + "Establishing user credentials.", progress, step, progressBar);
104
105 // Create credentials
106 boolean success = setupCredentialsFrame(username, profile);
107 if (!success) {
108 return CreateResult.ErrorIODuringCredentialsFrameSetup;
109 }
110
111 progress = progress(message + "Creating Individual Space.", progress, step, progressBar);
112
113 // Copy private resources to personal area
114 Path personalResources = createPersonalArea(username);
115
116 progress = progress(message + "Creating Space For Dead Drops.", progress, step, progressBar);
117
118 createDeaddropsArea(personalResources);
119
120 System.err.println("**** Hardwired call in Apollo's AuthioPathManager");
121 AudioPathManager.activateAndScanAudioDir(); // ****
122
123 progress = progress(message + "Done.", progress, step, progressBar);
124
125 return CreateResult.SuccessCreateAccount;
126 }
127
128 public static CreateResult createAlternativeAccess(Map<AuthenticationTag, String> userdata) {
129 String username = userdata.get(AuthenticationTag.Username);
130 FrameIO.CreateFrame(username, null, "default1");
131 return null;
132 }
133
134 public enum CreateResult {
135 SuccessCreateAccount ("Account created."),
136 SuccessAlternativeAccount ("Alternative access to account established."),
137 ErrorSymmetricKey ("An error occured while trying to generate your personal key."),
138 ErrorAsymmetricKeys ("An error occured while trying to generate asymmetric keys."),
139 ErrorNewProfile ("An error occured while creating the profile frames."),
140 ErrorCredentialsFrame ("Unable to establish credentials frame for new profile frame."),
141 ErrorIODuringCredentialsFrameSetup ("An error occured during the setup of the new users credentials frame.");
142
143 private String message = null;
144
145 private CreateResult(String message) {
146 this.message = message;
147 }
148
149 public String toString() {
150 return message;
151 }
152 }
153
154 private static int progress(String message, int progress, int step, Progress progressBar) {
155 try {
156 progressBar.UpdateMessage(message, progress += step);
157 } catch (Exception e) {
158 e.printStackTrace();
159 }
160 DisplayController.refreshBayArea();
161 return progress;
162 }
163
164 private static void createDeaddropsArea(Path personalResources) {
165 File deadDropsDir = new File(personalResources.resolve("deaddrops").toAbsolutePath().toString());
166 deadDropsDir.mkdir();
167 }
168
169 private static Path createPersonalArea(String username) {
170 Path personalResources = UserSettings.PublicAndPrivateResources ? FrameIO.setupPersonalResources(username) : Paths.get(FrameIO.PARENT_FOLDER);
171
172 File contactsDir = new File(personalResources.resolve("contacts").toAbsolutePath().toString());
173 contactsDir.mkdir();
174 return personalResources;
175 }
176
177 private static boolean setupCredentialsFrame(String username, Frame profile) {
178 try {
179 File credentialsDir = new File(profile.getFramesetPath() + username + "-credentials");
180 credentialsDir.mkdir();
181 // credentials.inf file.
182 String credentialsPath = credentialsDir.getAbsolutePath() + File.separator + "credentials.inf";
183 File credentialsFile = new File(credentialsPath);
184 credentialsFile.createNewFile();
185 FileWriter out = new FileWriter(credentialsFile);
186 out.write(AuthenticatorBrowser.CREDENTIALS_FRAME + ".exp");
187 out.flush();
188 out.close();
189 // migrate credentials frame
190 Frame credentialsFrame = FrameIO.LoadFrame(username + AuthenticatorBrowser.CREDENTIALS_FRAME);
191 Path destinationDirectory = Paths.get(credentialsDir.getAbsolutePath());
192 Path destinationFile = destinationDirectory.resolve(AuthenticatorBrowser.CREDENTIALS_FRAME + ExpReader.EXTENTION);
193 FrameIO.migrateFrame(credentialsFrame, destinationFile);
194 return true;
195 } catch (IOException e) {
196 return false;
197 }
198 }
199
200 private static Frame createNewProfile(String username, String email, String personalKey, String privateKey,
201 String publicKey) throws InvalidFramesetNameException, ExistingFramesetException {
202 // Establish the initial settings for the created user.
203 Map<String, Setting> initialSettings = new HashMap<String, Setting>();
204 initialSettings.put("settings.identity.secrets.PersonalKey", constructTextSetting("The AES key used to secure your profile frame - do not share with anyone!", "PersonalKey", personalKey));
205 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));
206 initialSettings.put("settings.identity.PublicKey", constructTextSetting("The RSA key used to decrypt things encrypted with your RSA public key.", "PublicKey", publicKey));
207 initialSettings.put("settings.identity.Email", constructGenericSetting(String.class, "Your public-facing email address.", "Email", email, username));
208 initialSettings.put("settings.UserSettings.UserName", constructGenericSetting(String.class, "Username", "Username", username, username));
209 initialSettings.put("settings.UserSettings.ProfileName", constructGenericSetting(String.class, "Profilename", "Profilename", username, username));
210 initialSettings.put("settings.UserSettings.HomeFrame", constructGenericSetting(String.class, "The home frame", "HomeFrame", username + 1, username));
211 initialSettings.put("org.expeditee.gui.folders.FolderSettings.FrameDirs", FolderSettings.FrameDirs);
212 initialSettings.put("org.expeditee.gui.folders.FolderSettings.ImageDirs", FolderSettings.ImageDirs);
213 initialSettings.put("org.expeditee.gui.folders.FolderSettings.AudioDirs", FolderSettings.AudioDirs);
214
215 // Record the credentials frame number and password colleagues frame
216 Map<String, Consumer<Frame>> notifiers = new HashMap<String, Consumer<Frame>>();
217 notifiers.put("settings.identity", frame -> {
218 AuthenticatorBrowser.CREDENTIALS_FRAME = frame.getNumber();
219 frame.addToData("MultiuserCredentials");
220 Collection<Text> textItems = frame.getTextItems();
221 for (Text t: textItems) {
222 if (t.getText().equals("Secrets")) {
223 t.setPermission(new PermissionPair(UserAppliedPermission.followLinks, UserAppliedPermission.denied));
224 break;
225 }
226 }
227 });
228 notifiers.put("settings.identity.passwordrecovery", frame -> {
229 AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME = frame.getNumber();
230 });
231
232
233 // Create users profile
234 Frame profile = FrameIO.CreateNewProfile(username, initialSettings, notifiers);
235 int lastNumber = FrameIO.getLastNumber(profile.getFramesetName());
236 for (int i = 1; i <= lastNumber; i++) {
237 Frame f = FrameIO.LoadFrame(profile.getFramesetName() + i);
238 Text titleItem = f.getTitleItem();
239 if (i == 1 && titleItem != null) {
240 titleItem.delete();
241 f.setBackgroundColor(new Colour(1, 1, 0.39f));
242 }
243 f.setOwner(username);
244 f.getAllItems().stream().forEach(item -> item.setOwner(username));
245 f.setChanged(true);
246 if (f.getNumber() != AuthenticatorBrowser.CREDENTIALS_FRAME &&
247 f.getNumber() != AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME) {
248 f.setEncryptionLabel(AuthenticatorBrowser.PROFILEENCRYPTIONLABEL);
249 }
250 Collection<Item> secretsLink = Actions.getByContent(f, "Secrets");
251 Collection<Item> publicKeyItem = Actions.getByContent(f, "PublicKey");
252 if (!secretsLink.isEmpty() && !publicKeyItem.isEmpty()) {
253 //Then we are on credentials frame
254 f.addToData("MultiuserCredentials");
255 }
256 Text backupPersonalKey = KeyList.PersonalKey.get();
257 Text tempPersonalKey = KeyList.PersonalKey.generateText();
258 tempPersonalKey.setData(personalKey);
259 KeyList.PersonalKey.setSetting(tempPersonalKey);
260 FrameIO.SaveFrame(f);
261 KeyList.PersonalKey.setSetting(backupPersonalKey);
262 }
263 return profile;
264 }
265
266 private static String generatePersonalKey(String username, String password) {
267 try {
268 Random rand = new SecureRandom();
269 byte[] keyBytes = new byte[16];
270 rand.nextBytes(keyBytes);
271 SecretKey key = new SecretKeySpec(keyBytes, SymmetricAlgorithm);
272 AuthenticatorBrowser.getInstance().putKey(username, password, key);
273 String personalKey = Base64.getEncoder().encodeToString(key.getEncoded());
274 return personalKey;
275 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | ClassNotFoundException
276 | IOException | SQLException e) {
277 return null;
278 }
279 }
280
281 private static String[] generateAsymmetricKeys() {
282 try {
283 KeyPairGenerator keyGen = KeyPairGenerator.getInstance(AsymmetricAlgorithm);
284 keyGen.initialize(1024);
285 KeyPair keyPair = keyGen.generateKeyPair();
286 String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
287 String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
288 return new String[] { privateKey, publicKey };
289 } catch (NoSuchAlgorithmException e) {
290 return null;
291 }
292 }
293
294 private static TextSetting constructTextSetting(String tooltip, String text, String data) {
295 return new TextSetting(tooltip, text) {
296 @Override
297 public Text generateText() {
298 Text t = new Text(text);
299 t.setData(data);
300 return t;
301 }
302 };
303 }
304
305 private static <T> GenericSetting<T> constructGenericSetting(Class<T> type, String tooltip, String name, T value, String frameset) {
306 return new GenericSetting<T>(type, tooltip, name, value) {
307 @Override
308 public Text generateRepresentation(String name, String frameset) {
309 Text t = new Text(name + ": " + value);
310 return t;
311 }
312 };
313 }
314}
Note: See TracBrowser for help on using the repository browser.