From 677ef4ddbe7e4727303d799a415543cb65426a76 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 24 Feb 2023 16:37:36 +0100 Subject: Allow connecting a new session to a one-time access code (Add support for browser-based login flow) --- .../org/openslx/imagemaster/localrpc/JsonUser.java | 20 +++++++++ .../imagemaster/localrpc/NetworkHandler.java | 19 ++++++--- .../openslx/imagemaster/session/AccessCode.java | 26 ++++++++++++ .../imagemaster/session/SessionManager.java | 49 ++++++++++++++++++++++ .../thrift/server/MasterServerHandler.java | 6 +++ 5 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/openslx/imagemaster/session/AccessCode.java diff --git a/src/main/java/org/openslx/imagemaster/localrpc/JsonUser.java b/src/main/java/org/openslx/imagemaster/localrpc/JsonUser.java index 9c86ea7..3dff32f 100644 --- a/src/main/java/org/openslx/imagemaster/localrpc/JsonUser.java +++ b/src/main/java/org/openslx/imagemaster/localrpc/JsonUser.java @@ -2,7 +2,9 @@ package org.openslx.imagemaster.localrpc; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.openslx.bwlp.thrift.iface.AuthorizationError; import org.openslx.bwlp.thrift.iface.Role; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.imagemaster.util.Util; @@ -17,9 +19,15 @@ public class JsonUser private String lastName = null; private String mail = null; private String role = null; + private String status; + private String error; + + private String accessCode; public UserInfo toUser() { + if ( !"ok".equals( status ) ) + return null; Role role; try { role = Role.valueOf( this.role ); @@ -35,4 +43,16 @@ public class JsonUser return ui; } + public TAuthorizationException toException() + { + if ( "ok".equals( status ) ) + return null; + return new TAuthorizationException( AuthorizationError.GENERIC_ERROR, this.error ); + } + + public String accessCode() + { + return this.accessCode; + } + } diff --git a/src/main/java/org/openslx/imagemaster/localrpc/NetworkHandler.java b/src/main/java/org/openslx/imagemaster/localrpc/NetworkHandler.java index 228d793..4416647 100644 --- a/src/main/java/org/openslx/imagemaster/localrpc/NetworkHandler.java +++ b/src/main/java/org/openslx/imagemaster/localrpc/NetworkHandler.java @@ -13,6 +13,7 @@ import java.util.concurrent.LinkedBlockingQueue; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.openslx.bwlp.thrift.iface.ClientSessionData; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.imagemaster.session.Session; import org.openslx.imagemaster.session.SessionManager; @@ -125,13 +126,19 @@ public class NetworkHandler implements Runnable { try { JsonUser ju = gson.fromJson( payload, JsonUser.class ); - UserInfo u = ju.toUser(); - if ( u == null ) { - log.warn( "Invalid or inomplete RPC data (" + payload + ")" ); - return "Invalid or incomplete RPC data"; + TAuthorizationException ex = ju.toException(); + if ( ex != null ) { + SessionManager.addAuthError( ex, ju.accessCode() ); + return "Auth error"; + } else { + UserInfo u = ju.toUser(); + if ( u == null ) { + log.warn( "Invalid or inomplete RPC data (" + payload + ")" ); + return "Invalid or incomplete RPC data"; + } + ClientSessionData sd = SessionManager.addSession( new Session( u ), ju.accessCode() ); + return "TOKEN:" + sd.authToken + " SESSIONID:" + sd.sessionId; } - ClientSessionData sd = SessionManager.addSession( new Session( u ) ); - return "TOKEN:" + sd.authToken + " SESSIONID:" + sd.sessionId; } catch ( Throwable t ) { log.error( "Exception on json decode", t ); } diff --git a/src/main/java/org/openslx/imagemaster/session/AccessCode.java b/src/main/java/org/openslx/imagemaster/session/AccessCode.java new file mode 100644 index 0000000..4407329 --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/session/AccessCode.java @@ -0,0 +1,26 @@ +package org.openslx.imagemaster.session; + +import org.openslx.bwlp.thrift.iface.ClientSessionData; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; + +public class AccessCode +{ + + public final long timeout = System.currentTimeMillis() + 600_000; + + public final ClientSessionData clientSession; + + public final TAuthorizationException ex; + + public AccessCode( ClientSessionData clientSession, TAuthorizationException ex ) + { + this.clientSession = clientSession; + this.ex = ex; + } + + public boolean timedOut() + { + return System.currentTimeMillis() > this.timeout; + } + +} diff --git a/src/main/java/org/openslx/imagemaster/session/SessionManager.java b/src/main/java/org/openslx/imagemaster/session/SessionManager.java index 57973ac..a7c7cb9 100644 --- a/src/main/java/org/openslx/imagemaster/session/SessionManager.java +++ b/src/main/java/org/openslx/imagemaster/session/SessionManager.java @@ -6,12 +6,15 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.openslx.bwlp.thrift.iface.ClientSessionData; import org.openslx.bwlp.thrift.iface.Satellite; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; +import org.openslx.bwlp.thrift.iface.TNotFoundException; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.imagemaster.db.mappers.DbSatellite; import org.openslx.imagemaster.util.Hash; @@ -29,6 +32,9 @@ public class SessionManager // Map of currently known sessions private static final Map sessions = new LinkedHashMap<>(); + // Map of pending "access code -> session" lookups + private static final Map accessCodes = new ConcurrentHashMap<>(); + public static ClientSessionData addSession( Session session ) { final String authToken = Hash.md5( UUID.randomUUID().toString() ); @@ -48,6 +54,20 @@ public class SessionManager return new ClientSessionData( sessionId, authToken, sats, ui ); } + public static ClientSessionData addSession( Session session, String accessToken ) + { + ClientSessionData s = addSession( session ); + if ( accessToken != null ) { + accessCodes.put( accessToken, new AccessCode( s, null ) ); + } + return s; + } + + public static void addAuthError( TAuthorizationException ex, String accessToken ) + { + accessCodes.put( accessToken, new AccessCode( null, ex ) ); + } + static { QuickTimer.scheduleAtFixedDelay( new Task() { @Override @@ -62,10 +82,20 @@ public class SessionManager } } } + Iterator it = accessCodes.values().iterator(); + while ( it.hasNext() ) { + final AccessCode s = it.next(); + if ( s.timedOut() ) { + it.remove(); + } + } } }, 123, TimeUnit.MINUTES.toMillis( 13 ) ); } + /** + * Get from userToken, known to satellite servers. + */ public static Session getSessionFromToken( String token ) { if ( token == null || token.length() != 32 ) { @@ -82,6 +112,9 @@ public class SessionManager return session; } + /** + * Get from sessionId, only known by client/user and us. + */ public static Session getSessionFromSessionId( String sessionId ) { if ( sessionId == null || sessionId.length() != 64 ) { @@ -125,4 +158,20 @@ public class SessionManager } } + /** + * Get the according session data (satToken, masterToken) for given access code, which was + * supplied by the client earlier. This can only be done once; retrieving the session will remove + * the entry from the lookup table. + */ + public static ClientSessionData getSessionFromAccessCode( String accessCode ) + throws TNotFoundException, TAuthorizationException + { + AccessCode data = accessCodes.remove( accessCode ); + if ( data == null ) + throw new TNotFoundException(); + if ( data.ex != null ) + throw data.ex; + return data.clientSession; + } + } diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java b/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java index 1a41e2d..d423c36 100644 --- a/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java +++ b/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java @@ -172,6 +172,12 @@ public class MasterServerHandler implements MasterServer.Iface SessionManager.invalidate( sessionId ); } + @Override + public ClientSessionData getSessionFromAccessCode( String accessCode ) throws TNotFoundException, TException + { + return SessionManager.getSessionFromAccessCode( accessCode ); + } + /** * Request information about user for given token * -- cgit v1.2.3-55-g7522