Browse Source

implemented trust option

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
sqlite
Stephan Richter 2 months ago
parent
commit
a8b476264c
  1. 12
      de.srsoftware.http/src/main/java/de/srsoftware/http/SessionToken.java
  2. 1
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java
  3. 2
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/SessionService.java
  4. 2
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Session.java
  5. 8
      de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/SessionServiceTest.java
  6. 9
      de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/UserController.java
  7. 17
      de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java
  8. 21
      de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteSessionService.java
  9. 2
      de.srsoftware.oidc.web/src/main/resources/en/login.html
  10. 4
      de.srsoftware.oidc.web/src/main/resources/en/scripts/login.js
  11. 1
      de.srsoftware.oidc.web/src/main/resources/en/todo.html

12
de.srsoftware.http/src/main/java/de/srsoftware/http/SessionToken.java

@ -4,7 +4,6 @@ package de.srsoftware.http; @@ -4,7 +4,6 @@ package de.srsoftware.http;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
@ -12,14 +11,19 @@ import java.util.Optional; @@ -12,14 +11,19 @@ import java.util.Optional;
public class SessionToken extends Cookie {
private final String sessionId;
private final String sessionId;
private static final DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss O");
public SessionToken(String sessionId, Instant expiration){
super("sessionToken", "%s; Path=/api; Expires=%s".formatted(sessionId,FORMAT.format(expiration.atZone(ZoneOffset.UTC))));
public SessionToken(String sessionId, Instant expiration, boolean trust) {
super("sessionToken", sessionToken(sessionId, expiration, trust));
this.sessionId = sessionId;
}
private static String sessionToken(String sessionId, Instant expiration, boolean trust) {
if (trust) return "%s; Path=/api; Expires=%s".formatted(sessionId, FORMAT.format(expiration.atZone(ZoneOffset.UTC)));
return "%s; Path=/api".formatted(sessionId);
}
public SessionToken(String sessionId) {
super("sessionToken", sessionId + "; Path=/api");
this.sessionId = sessionId;

1
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java

@ -51,6 +51,7 @@ public class Constants { @@ -51,6 +51,7 @@ public class Constants {
public static final String START_TLS = "start_tls";
public static final String TOKEN = "token";
public static final String TOKEN_TYPE = "token_type";
public static final String TRUST = "trust";
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
public static final String USER = "user";
public static final String USER_ID = "user_id";

2
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/SessionService.java

@ -6,7 +6,7 @@ import de.srsoftware.oidc.api.data.User; @@ -6,7 +6,7 @@ import de.srsoftware.oidc.api.data.User;
import java.util.Optional;
public interface SessionService {
Session createSession(User user);
Session createSession(User user, boolean trustBrowser);
SessionService dropSession(String sessionId);
Session extend(Session session, User user);
Optional<Session> retrieve(String sessionId);

2
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/data/Session.java

@ -3,5 +3,5 @@ package de.srsoftware.oidc.api.data; @@ -3,5 +3,5 @@ package de.srsoftware.oidc.api.data;
import java.time.Instant;
public record Session(String userId, Instant expiration, String id) {
public record Session(String userId, Instant expiration, String id, boolean trustBrowser) {
}

8
de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/SessionServiceTest.java

@ -41,7 +41,7 @@ public abstract class SessionServiceTest { @@ -41,7 +41,7 @@ public abstract class SessionServiceTest {
var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofMinutes(5));
Instant now = Instant.now();
var session = sessionService().createSession(user);
var session = sessionService().createSession(user, false);
var expiration = session.expiration();
assertTrue(expiration.isAfter(now.plus(5, ChronoUnit.MINUTES).minusSeconds(1)));
assertTrue(expiration.isBefore(now.plus(5, ChronoUnit.MINUTES).plusSeconds(1)));
@ -57,7 +57,7 @@ public abstract class SessionServiceTest { @@ -57,7 +57,7 @@ public abstract class SessionServiceTest {
var pass = hasher().hash(PASSWORD, uuid);
var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofMinutes(5));
var session = sessionService().createSession(user);
var session = sessionService().createSession(user, false);
Instant now = Instant.now();
sessionService().extend(session, user.sessionDuration(Duration.ofMinutes(10)));
@ -75,7 +75,7 @@ public abstract class SessionServiceTest { @@ -75,7 +75,7 @@ public abstract class SessionServiceTest {
var pass = hasher().hash(PASSWORD, uuid);
var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofMinutes(5));
var session = sessionService().createSession(user);
var session = sessionService().createSession(user, false);
assertTrue(sessionService().retrieve(session.id()).isPresent());
sessionService().dropSession(session.id());
@ -89,7 +89,7 @@ public abstract class SessionServiceTest { @@ -89,7 +89,7 @@ public abstract class SessionServiceTest {
var pass = hasher().hash(PASSWORD, uuid);
var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofSeconds(2));
var session = sessionService().createSession(user);
var session = sessionService().createSession(user, false);
assertTrue(sessionService().retrieve(session.id()).isPresent());
Thread.sleep(2500);

9
de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/UserController.java

@ -193,9 +193,10 @@ public class UserController extends Controller { @@ -193,9 +193,10 @@ public class UserController extends Controller {
var username = body.has(USERNAME) ? body.getString(USERNAME) : null;
var password = body.has(PASSWORD) ? body.getString(PASSWORD) : null;
var trust = body.has(TRUST) ? body.getBoolean(TRUST) : false;
Optional<User> user = users.load(username, password);
if (user.isPresent()) return sendUserAndCookie(ex, sessions.createSession(user.get()), user.get());
if (user.isPresent()) return sendUserAndCookie(ex, sessions.createSession(user.get(), trust), user.get());
return sendEmptyResponse(HTTP_UNAUTHORIZED, ex);
}
@ -224,8 +225,8 @@ public class UserController extends Controller { @@ -224,8 +225,8 @@ public class UserController extends Controller {
if (optUser.isEmpty()) return sendContent(ex, HTTP_UNAUTHORIZED, "invalid token");
var user = optUser.get();
users.updatePassword(user, newPass);
var session = sessions.createSession(user);
new SessionToken(session.id(),session.expiration()).addTo(ex);
var session = sessions.createSession(user, false);
new SessionToken(session.id(), session.expiration(), session.trustBrowser()).addTo(ex);
return sendRedirect(ex, "/");
}
@ -266,7 +267,7 @@ public class UserController extends Controller { @@ -266,7 +267,7 @@ public class UserController extends Controller {
}
private boolean sendUserAndCookie(HttpExchange ex, Session session, User user) throws IOException {
new SessionToken(session.id(),session.expiration()).addTo(ex);
new SessionToken(session.id(), session.expiration(), session.trustBrowser()).addTo(ex);
return sendContent(ex, user.map(false));
}

17
de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java

@ -221,10 +221,10 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe @@ -221,10 +221,10 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
// TODO: drop expired sessions
@Override
public Session createSession(User user) {
public Session createSession(User user, boolean trustBrowser) {
var now = Instant.now();
var endOfSession = now.plus(user.sessionDuration()).truncatedTo(SECONDS);
return save(new Session(user.uuid(), endOfSession, uuid()));
return save(new Session(user.uuid(), endOfSession, uuid(), trustBrowser));
}
@Override
@ -237,7 +237,7 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe @@ -237,7 +237,7 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
@Override
public Session extend(Session session, User user) {
var endOfSession = Instant.now().plus(user.sessionDuration());
return save(new Session(user.uuid(), endOfSession, session.id()));
return save(new Session(user.uuid(), endOfSession, session.id(), session.trustBrowser()));
}
private JSONObject sessions() {
@ -248,10 +248,11 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe @@ -248,10 +248,11 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
@Override
public Optional<Session> retrieve(String sessionId) {
try {
var session = sessions().getJSONObject(sessionId);
var userId = session.getString(USER);
var expiration = Instant.ofEpochSecond(session.getLong(EXPIRATION)).truncatedTo(SECONDS);
if (expiration.isAfter(Instant.now())) return Optional.of(new Session(userId, expiration, sessionId));
var session = sessions().getJSONObject(sessionId);
var userId = session.getString(USER);
var expiration = Instant.ofEpochSecond(session.getLong(EXPIRATION)).truncatedTo(SECONDS);
var trustBrowser = session.getBoolean(TRUST);
if (expiration.isAfter(Instant.now())) return Optional.of(new Session(userId, expiration, sessionId, trustBrowser));
dropSession(sessionId);
} catch (Exception ignored) {
}
@ -259,7 +260,7 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe @@ -259,7 +260,7 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
}
private Session save(Session session) {
sessions().put(session.id(), Map.of(USER, session.userId(), EXPIRATION, session.expiration().getEpochSecond()));
sessions().put(session.id(), Map.of(USER, session.userId(), EXPIRATION, session.expiration().getEpochSecond(), TRUST, session.trustBrowser()));
save();
return session;
}

21
de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteSessionService.java

@ -18,8 +18,8 @@ public class SqliteSessionService extends SqliteStore implements SessionService @@ -18,8 +18,8 @@ public class SqliteSessionService extends SqliteStore implements SessionService
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'";
private static final String SET_STORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = '" + STORE_VERSION + "'";
private static final String CREATE_SESSION_TABLE = "CREATE TABLE sessions (id VARCHAR(64) PRIMARY KEY, userId VARCHAR(64) NOT NULL, expiration LONG NOT NULL)";
private static final String SAVE_SESSION = "INSERT INTO sessions (id, userId, expiration) VALUES (?,?,?) ON CONFLICT DO UPDATE SET expiration = ?;";
private static final String CREATE_SESSION_TABLE = "CREATE TABLE sessions (id VARCHAR(64) PRIMARY KEY, userId VARCHAR(64) NOT NULL, expiration LONG NOT NULL, trust_browser BOOLEAN DEFAULT false)";
private static final String SAVE_SESSION = "INSERT INTO sessions (id, userId, expiration, trust_browser) VALUES (?,?,?, ?) ON CONFLICT DO UPDATE SET expiration = ?, trust_browser = ?;";
private static final String DROP_SESSION = "DELETE FROM sessions WHERE id = ?";
private static final String SELECT_SESSION = "SELECT * FROM sessions WHERE id = ?";
@ -28,10 +28,10 @@ public class SqliteSessionService extends SqliteStore implements SessionService @@ -28,10 +28,10 @@ public class SqliteSessionService extends SqliteStore implements SessionService
}
@Override
public Session createSession(User user) {
public Session createSession(User user, boolean trustBrowser) {
var now = Instant.now();
var endOfSession = now.plus(user.sessionDuration()).truncatedTo(SECONDS);
return save(new Session(user.uuid(), endOfSession, uuid()));
return save(new Session(user.uuid(), endOfSession, uuid(), trustBrowser));
}
private void createStoreTables() throws SQLException {
@ -53,7 +53,7 @@ public class SqliteSessionService extends SqliteStore implements SessionService @@ -53,7 +53,7 @@ public class SqliteSessionService extends SqliteStore implements SessionService
@Override
public Session extend(Session session, User user) {
var endOfSession = Instant.now().plus(user.sessionDuration());
return save(new Session(user.uuid(), endOfSession, session.id()));
return save(new Session(user.uuid(), endOfSession, session.id(), session.trustBrowser()));
}
@Override
@ -99,9 +99,10 @@ public class SqliteSessionService extends SqliteStore implements SessionService @@ -99,9 +99,10 @@ public class SqliteSessionService extends SqliteStore implements SessionService
var rs = stmt.executeQuery();
Optional<Session> result = Optional.empty();
if (rs.next()) {
var userID = rs.getString("userId");
var expiration = Instant.ofEpochSecond(rs.getLong("expiration"));
if (expiration.isAfter(Instant.now())) result = Optional.of(new Session(userID, expiration, sessionId));
var userID = rs.getString("userId");
var expiration = Instant.ofEpochSecond(rs.getLong("expiration"));
var trustBrowser = rs.getBoolean("trust_browser");
if (expiration.isAfter(Instant.now())) result = Optional.of(new Session(userID, expiration, sessionId, trustBrowser));
}
rs.close();
return result;
@ -117,7 +118,9 @@ public class SqliteSessionService extends SqliteStore implements SessionService @@ -117,7 +118,9 @@ public class SqliteSessionService extends SqliteStore implements SessionService
stmt.setString(1, session.id());
stmt.setString(2, session.userId());
stmt.setLong(3, expiration);
stmt.setLong(4, expiration);
stmt.setBoolean(4, session.trustBrowser());
stmt.setLong(5, expiration);
stmt.setBoolean(6, session.trustBrowser());
stmt.execute();
return session;
} catch (SQLException e) {

2
de.srsoftware.oidc.web/src/main/resources/en/login.html

@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
<tr>
<td colspan="2">
<label>
<input type="checkbox" name="trust" checked="checked"/>
<input type="checkbox" id="trust" name="trust" checked="checked"/>
Quit session when browser is closed.
</label>
</td>

4
de.srsoftware.oidc.web/src/main/resources/en/scripts/login.js

@ -46,6 +46,7 @@ function resetPw(){ @@ -46,6 +46,7 @@ function resetPw(){
function tryLogin(){
var username = getValue('username');
var password = getValue('password');
var trust = !get('trust').checked;
fetch(user_controller+"/login",{
method: 'POST',
headers: {
@ -54,7 +55,8 @@ function tryLogin(){ @@ -54,7 +55,8 @@ function tryLogin(){
},
body: JSON.stringify({
username : username,
password : password
password : password,
trust: trust
})
}).then(handleLogin);
return false;

1
de.srsoftware.oidc.web/src/main/resources/en/todo.html

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
<li>implement token refresh</li>
<li>Verschlüsselung im config-File</li>
<li>Configuration im Frontend</li>
<li>Process <em>quit session when browser is closed</em> input on login</li>
</ul>
</div>
</body>

Loading…
Cancel
Save