|
|
|
@ -2,24 +2,28 @@
@@ -2,24 +2,28 @@
|
|
|
|
|
package de.srsoftware.oidc.datastore.file; /* © SRSoftware 2024 */ |
|
|
|
|
import static de.srsoftware.oidc.api.User.*; |
|
|
|
|
|
|
|
|
|
import de.srsoftware.oidc.api.PasswordHasher; |
|
|
|
|
import de.srsoftware.oidc.api.User; |
|
|
|
|
import de.srsoftware.oidc.api.UserService; |
|
|
|
|
import de.srsoftware.oidc.api.*; |
|
|
|
|
import java.io.File; |
|
|
|
|
import java.io.FileNotFoundException; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.nio.file.Files; |
|
|
|
|
import java.nio.file.Path; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Optional; |
|
|
|
|
import java.time.Duration; |
|
|
|
|
import java.time.Instant; |
|
|
|
|
import java.time.temporal.ChronoUnit; |
|
|
|
|
import java.util.*; |
|
|
|
|
import org.json.JSONObject; |
|
|
|
|
|
|
|
|
|
public class FileStore implements UserService { |
|
|
|
|
public class FileStore implements SessionService, UserService { |
|
|
|
|
private static final String EXPIRATION = "expiration"; |
|
|
|
|
private static final String SESSIONS = "sessions"; |
|
|
|
|
private static final String USERS = "users"; |
|
|
|
|
private static final String USER = "user"; |
|
|
|
|
|
|
|
|
|
private final Path storageFile; |
|
|
|
|
private final JSONObject json; |
|
|
|
|
private final PasswordHasher<String> passwordHasher; |
|
|
|
|
private Duration sessionDuration = Duration.of(10, ChronoUnit.MINUTES); |
|
|
|
|
|
|
|
|
|
public FileStore(File storage, PasswordHasher<String> passwordHasher) throws IOException { |
|
|
|
|
this.storageFile = storage.toPath(); |
|
|
|
@ -33,6 +37,47 @@ public class FileStore implements UserService {
@@ -33,6 +37,47 @@ public class FileStore implements UserService {
|
|
|
|
|
json = new JSONObject(Files.readString(storageFile)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private FileStore save() { |
|
|
|
|
try { |
|
|
|
|
Files.writeString(storageFile, json.toString(2)); |
|
|
|
|
return this; |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
throw new RuntimeException(e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*** User Service Methods ***/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public UserService delete(User user) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public FileStore init(User defaultUser) { |
|
|
|
|
if (!json.has(SESSIONS)) json.put(SESSIONS, new JSONObject()); |
|
|
|
|
if (!json.has(USERS)) save(defaultUser); |
|
|
|
|
return this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public List<User> list() { |
|
|
|
|
return List.of(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Optional<User> load(String userId) { |
|
|
|
|
try { |
|
|
|
|
var users = json.getJSONObject(USERS); |
|
|
|
|
var user = users.getJSONObject(userId); |
|
|
|
|
return Optional.of(new User(user.getString(USERNAME), user.getString(PASSWORD), user.getString(REALNAME), user.getString(EMAIL), userId)); |
|
|
|
|
} catch (Exception ignored) { |
|
|
|
|
} |
|
|
|
|
return Optional.empty(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Optional<User> load(String username, String password) { |
|
|
|
|
try { |
|
|
|
@ -53,35 +98,59 @@ public class FileStore implements UserService {
@@ -53,35 +98,59 @@ public class FileStore implements UserService {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public UserService delete(User user) { |
|
|
|
|
public FileStore save(User user) { |
|
|
|
|
JSONObject users; |
|
|
|
|
if (!json.has(USERS)) { |
|
|
|
|
json.put(USERS, users = new JSONObject()); |
|
|
|
|
} else { |
|
|
|
|
users = json.getJSONObject(USERS); |
|
|
|
|
} |
|
|
|
|
users.put(user.uuid(), user.map(true)); |
|
|
|
|
return save(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*** Session Service Methods ***/ |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Session createSession(User user) { |
|
|
|
|
var now = Instant.now(); |
|
|
|
|
var endOfSession = now.plus(sessionDuration); |
|
|
|
|
return save(new Session(user, endOfSession, UUID.randomUUID().toString())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public SessionService dropSession(String sessionId) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public UserService init(User defaultUser) { |
|
|
|
|
if (!json.has(USERS)) save(defaultUser); |
|
|
|
|
return this; |
|
|
|
|
public Session extend(String sessionId) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public UserService save(User user) { |
|
|
|
|
JSONObject users; |
|
|
|
|
if (!json.has(USERS)) { |
|
|
|
|
json.put(USERS, users = new JSONObject()); |
|
|
|
|
} else |
|
|
|
|
users = json.getJSONObject(USERS); |
|
|
|
|
users.put(user.uuid(), user.map(true)); |
|
|
|
|
public Optional<Session> retrieve(String sessionId) { |
|
|
|
|
var sessions = json.getJSONObject(SESSIONS); |
|
|
|
|
try { |
|
|
|
|
Files.writeString(storageFile, json.toString(2)); |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
throw new RuntimeException(e); |
|
|
|
|
var session = sessions.getJSONObject(sessionId); |
|
|
|
|
var userId = session.getString(USER); |
|
|
|
|
var expiration = Instant.ofEpochSecond(session.getLong(EXPIRATION)); |
|
|
|
|
if (expiration.isAfter(Instant.now())) { |
|
|
|
|
return load(userId).map(user -> new Session(user, expiration, sessionId)); |
|
|
|
|
} |
|
|
|
|
return this; |
|
|
|
|
} catch (Exception ignored) { |
|
|
|
|
} |
|
|
|
|
return Optional.empty(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Session save(Session session) { |
|
|
|
|
json.getJSONObject(SESSIONS).put(session.id(), Map.of(USER, session.user().uuid(), EXPIRATION, session.expiration().getEpochSecond())); |
|
|
|
|
save(); |
|
|
|
|
return session; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public List<User> list() { |
|
|
|
|
return List.of(); |
|
|
|
|
public SessionService setDuration(Duration duration) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|