-
Notifications
You must be signed in to change notification settings - Fork 2
/
PermissionManager.java
114 lines (102 loc) · 4.36 KB
/
PermissionManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package foundationdb_fslayer.permissions;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.directory.DirectoryLayer;
import com.apple.foundationdb.directory.DirectorySubspace;
import com.apple.foundationdb.tuple.Tuple;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
// ./AUTH
// Username -> Password Hash
// ./IDMAP
// Username -> UserId
public class PermissionManager {
private static final List<String> ROOT_PATH = Arrays.asList(".");
private static final List<String> AUTH_PATH = Arrays.asList(".", "AUTH");
private static final List<String> ID_MAP_PATH = Arrays.asList(".", "IDMAP");
private static final String ID_COUNTER_KEY = "ID_COUNTER";
private static final long INITIAL_ID = 70000;
private final long currentUserId;
private PermissionManager(long id) {
this.currentUserId = id;
}
public static Optional<PermissionManager> login(
String username,
String password,
DirectoryLayer directoryLayer,
Transaction transaction
) {
byte[] passwordHash = hashPassword(password);
if (passwordHash == null) {
return Optional.empty();
}
try {
// Open needed subspaces
DirectorySubspace authSpace = directoryLayer.createOrOpen(transaction, AUTH_PATH).get();
DirectorySubspace idSpace = directoryLayer.createOrOpen(transaction, ID_MAP_PATH).get();
// Check if username exists in ./AUTH.
byte[] storedHash = transaction.get(authSpace.pack(username)).get();
if (storedHash != null) {
// If so, compare to the hash of the given password
if (!Arrays.equals(passwordHash, storedHash)) {
// If incorrect, fail
System.err.println("Incorrect password");
return Optional.empty();
}
// Otherwise, load the user id
long id = Tuple.fromBytes(transaction.get(idSpace.pack(username)).get()).getLong(0);
return Optional.of(new PermissionManager(id));
} else {
// Otherwise, add a new entry and generate a new id.
return getNewUserId(directoryLayer, transaction).map(id -> {
transaction.set(idSpace.pack(username), Tuple.from(id).pack());
transaction.set(authSpace.pack(username), passwordHash);
return new PermissionManager(id);
});
}
} catch (Exception e) {
System.err.println("Error loading login information");
e.printStackTrace();
return Optional.empty();
}
}
private static byte[] hashPassword(String password) {
try {
KeySpec spec = new PBEKeySpec(password.toCharArray(), new byte[16], 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return factory.generateSecret(spec).getEncoded();
} catch (Exception e) {
System.err.println("Hashing algorithm not found or spec invalid. All login attempts will fail.");
e.printStackTrace();
return null;
}
}
private static Optional<Long> getNewUserId(DirectoryLayer directoryLayer, Transaction tr) {
try {
DirectorySubspace rootSpace = directoryLayer.open(tr, ROOT_PATH).get();
byte[] rawCounterVal = tr.get(rootSpace.pack(ID_COUNTER_KEY)).get();
long currentCount ;
if (rawCounterVal == null) {
currentCount = INITIAL_ID;
} else {
currentCount = Tuple.fromBytes(rawCounterVal).getLong(0);
}
long newUserId = currentCount + 1;
tr.set(rootSpace.pack(ID_COUNTER_KEY), Tuple.from(newUserId).pack());
return Optional.of(newUserId);
} catch (Exception e) {
System.err.println("Error computing new user id");
e.printStackTrace();
return Optional.empty();
}
}
public long getId() {
return this.currentUserId;
}
}