Browse Source

implemented trust option

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
sqlite
Stephan Richter 2 months ago
parent
commit
a8b476264c
  1. 10
      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. 11
      de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java
  8. 17
      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

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

@ -4,7 +4,6 @@ package de.srsoftware.http;
import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -15,11 +14,16 @@ 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"); private static final DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss O");
public SessionToken(String sessionId, Instant expiration){ public SessionToken(String sessionId, Instant expiration, boolean trust) {
super("sessionToken", "%s; Path=/api; Expires=%s".formatted(sessionId,FORMAT.format(expiration.atZone(ZoneOffset.UTC)))); super("sessionToken", sessionToken(sessionId, expiration, trust));
this.sessionId = sessionId; 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) { public SessionToken(String sessionId) {
super("sessionToken", sessionId + "; Path=/api"); super("sessionToken", sessionId + "; Path=/api");
this.sessionId = sessionId; this.sessionId = sessionId;

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

@ -51,6 +51,7 @@ public class Constants {
public static final String START_TLS = "start_tls"; public static final String START_TLS = "start_tls";
public static final String TOKEN = "token"; public static final String TOKEN = "token";
public static final String TOKEN_TYPE = "token_type"; 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 UNAUTHORIZED_CLIENT = "unauthorized_client";
public static final String USER = "user"; public static final String USER = "user";
public static final String USER_ID = "user_id"; 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;
import java.util.Optional; import java.util.Optional;
public interface SessionService { public interface SessionService {
Session createSession(User user); Session createSession(User user, boolean trustBrowser);
SessionService dropSession(String sessionId); SessionService dropSession(String sessionId);
Session extend(Session session, User user); Session extend(Session session, User user);
Optional<Session> retrieve(String sessionId); 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;
import java.time.Instant; 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 {
var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofMinutes(5)); var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofMinutes(5));
Instant now = Instant.now(); Instant now = Instant.now();
var session = sessionService().createSession(user); var session = sessionService().createSession(user, false);
var expiration = session.expiration(); var expiration = session.expiration();
assertTrue(expiration.isAfter(now.plus(5, ChronoUnit.MINUTES).minusSeconds(1))); assertTrue(expiration.isAfter(now.plus(5, ChronoUnit.MINUTES).minusSeconds(1)));
assertTrue(expiration.isBefore(now.plus(5, ChronoUnit.MINUTES).plusSeconds(1))); assertTrue(expiration.isBefore(now.plus(5, ChronoUnit.MINUTES).plusSeconds(1)));
@ -57,7 +57,7 @@ public abstract class SessionServiceTest {
var pass = hasher().hash(PASSWORD, uuid); var pass = hasher().hash(PASSWORD, uuid);
var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofMinutes(5)); 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(); Instant now = Instant.now();
sessionService().extend(session, user.sessionDuration(Duration.ofMinutes(10))); sessionService().extend(session, user.sessionDuration(Duration.ofMinutes(10)));
@ -75,7 +75,7 @@ public abstract class SessionServiceTest {
var pass = hasher().hash(PASSWORD, uuid); var pass = hasher().hash(PASSWORD, uuid);
var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofMinutes(5)); 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()); assertTrue(sessionService().retrieve(session.id()).isPresent());
sessionService().dropSession(session.id()); sessionService().dropSession(session.id());
@ -89,7 +89,7 @@ public abstract class SessionServiceTest {
var pass = hasher().hash(PASSWORD, uuid); var pass = hasher().hash(PASSWORD, uuid);
var user = new User(USERNAME, pass, REALNAME, EMAIL, uuid).sessionDuration(Duration.ofSeconds(2)); 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()); assertTrue(sessionService().retrieve(session.id()).isPresent());
Thread.sleep(2500); 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 {
var username = body.has(USERNAME) ? body.getString(USERNAME) : null; var username = body.has(USERNAME) ? body.getString(USERNAME) : null;
var password = body.has(PASSWORD) ? body.getString(PASSWORD) : 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); 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); return sendEmptyResponse(HTTP_UNAUTHORIZED, ex);
} }
@ -224,8 +225,8 @@ public class UserController extends Controller {
if (optUser.isEmpty()) return sendContent(ex, HTTP_UNAUTHORIZED, "invalid token"); if (optUser.isEmpty()) return sendContent(ex, HTTP_UNAUTHORIZED, "invalid token");
var user = optUser.get(); var user = optUser.get();
users.updatePassword(user, newPass); users.updatePassword(user, newPass);
var session = sessions.createSession(user); var session = sessions.createSession(user, false);
new SessionToken(session.id(),session.expiration()).addTo(ex); new SessionToken(session.id(), session.expiration(), session.trustBrowser()).addTo(ex);
return sendRedirect(ex, "/"); return sendRedirect(ex, "/");
} }
@ -266,7 +267,7 @@ public class UserController extends Controller {
} }
private boolean sendUserAndCookie(HttpExchange ex, Session session, User user) throws IOException { 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)); return sendContent(ex, user.map(false));
} }

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

17
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
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'"; 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 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 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) VALUES (?,?,?) ON CONFLICT DO UPDATE SET expiration = ?;"; 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 DROP_SESSION = "DELETE FROM sessions WHERE id = ?";
private static final String SELECT_SESSION = "SELECT * 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
} }
@Override @Override
public Session createSession(User user) { public Session createSession(User user, boolean trustBrowser) {
var now = Instant.now(); var now = Instant.now();
var endOfSession = now.plus(user.sessionDuration()).truncatedTo(SECONDS); 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 { private void createStoreTables() throws SQLException {
@ -53,7 +53,7 @@ public class SqliteSessionService extends SqliteStore implements SessionService
@Override @Override
public Session extend(Session session, User user) { public Session extend(Session session, User user) {
var endOfSession = Instant.now().plus(user.sessionDuration()); 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 @Override
@ -101,7 +101,8 @@ public class SqliteSessionService extends SqliteStore implements SessionService
if (rs.next()) { if (rs.next()) {
var userID = rs.getString("userId"); var userID = rs.getString("userId");
var expiration = Instant.ofEpochSecond(rs.getLong("expiration")); var expiration = Instant.ofEpochSecond(rs.getLong("expiration"));
if (expiration.isAfter(Instant.now())) result = Optional.of(new Session(userID, expiration, sessionId)); var trustBrowser = rs.getBoolean("trust_browser");
if (expiration.isAfter(Instant.now())) result = Optional.of(new Session(userID, expiration, sessionId, trustBrowser));
} }
rs.close(); rs.close();
return result; return result;
@ -117,7 +118,9 @@ public class SqliteSessionService extends SqliteStore implements SessionService
stmt.setString(1, session.id()); stmt.setString(1, session.id());
stmt.setString(2, session.userId()); stmt.setString(2, session.userId());
stmt.setLong(3, expiration); stmt.setLong(3, expiration);
stmt.setLong(4, expiration); stmt.setBoolean(4, session.trustBrowser());
stmt.setLong(5, expiration);
stmt.setBoolean(6, session.trustBrowser());
stmt.execute(); stmt.execute();
return session; return session;
} catch (SQLException e) { } catch (SQLException e) {

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

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

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

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

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

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

Loading…
Cancel
Save