openvidu-server: token management refactoring

pull/391/head
pabloFuente 2020-01-31 14:59:46 +01:00
parent e663680b48
commit 35d8490180
7 changed files with 69 additions and 82 deletions

View File

@ -27,6 +27,9 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function; import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@ -42,9 +45,12 @@ import io.openvidu.server.recording.service.RecordingManager;
public class Session implements SessionInterface { public class Session implements SessionInterface {
private static final Logger log = LoggerFactory.getLogger(Session.class);
protected OpenviduConfig openviduConfig; protected OpenviduConfig openviduConfig;
protected RecordingManager recordingManager; protected RecordingManager recordingManager;
protected ConcurrentMap<String, Token> tokens = new ConcurrentHashMap<>();
protected final ConcurrentMap<String, Participant> participants = new ConcurrentHashMap<>(); protected final ConcurrentMap<String, Participant> participants = new ConcurrentHashMap<>();
protected String sessionId; protected String sessionId;
protected SessionProperties sessionProperties; protected SessionProperties sessionProperties;
@ -72,6 +78,7 @@ public class Session implements SessionInterface {
this.sessionProperties = previousSession.getSessionProperties(); this.sessionProperties = previousSession.getSessionProperties();
this.openviduConfig = previousSession.openviduConfig; this.openviduConfig = previousSession.openviduConfig;
this.recordingManager = previousSession.recordingManager; this.recordingManager = previousSession.recordingManager;
this.tokens = previousSession.tokens;
} }
public Session(String sessionId, SessionProperties sessionProperties, OpenviduConfig openviduConfig, public Session(String sessionId, SessionProperties sessionProperties, OpenviduConfig openviduConfig,
@ -132,6 +139,24 @@ public class Session implements SessionInterface {
this.activePublishers.decrementAndGet(); this.activePublishers.decrementAndGet();
} }
public void storeToken(Token token) {
this.tokens.put(token.getToken(), token);
}
public boolean isTokenValid(String token) {
return this.tokens.containsKey(token);
}
public Token consumeToken(String token) {
Token tokenObj = this.tokens.remove(token);
showTokens("Token consumed");
return tokenObj;
}
public void showTokens(String preMessage) {
log.info("{} { Session: {} | Tokens: {} }", preMessage, this.sessionId, this.tokens.keySet().toString());
}
public boolean isClosed() { public boolean isClosed() {
return closed; return closed;
} }

View File

@ -89,7 +89,6 @@ public abstract class SessionManager {
protected ConcurrentMap<String, ConcurrentLinkedQueue<CDREventRecording>> sessionidAccumulatedRecordings = new ConcurrentHashMap<>(); protected ConcurrentMap<String, ConcurrentLinkedQueue<CDREventRecording>> sessionidAccumulatedRecordings = new ConcurrentHashMap<>();
protected ConcurrentMap<String, Boolean> insecureUsers = new ConcurrentHashMap<>(); protected ConcurrentMap<String, Boolean> insecureUsers = new ConcurrentHashMap<>();
public ConcurrentMap<String, ConcurrentHashMap<String, Token>> sessionidTokenTokenobj = new ConcurrentHashMap<>();
public abstract void joinRoom(Participant participant, String sessionId, Integer transactionId); public abstract void joinRoom(Participant participant, String sessionId, Integer transactionId);
@ -281,51 +280,29 @@ public abstract class SessionManager {
final String sessionId = sessionNotActive.getSessionId(); final String sessionId = sessionNotActive.getSessionId();
this.sessionsNotActive.put(sessionId, sessionNotActive); this.sessionsNotActive.put(sessionId, sessionNotActive);
this.initializeCollections(sessionId); this.initializeCollections(sessionId);
showTokens();
return sessionNotActive; return sessionNotActive;
} }
public String newToken(String sessionId, OpenViduRole role, String serverMetadata, public String newToken(Session session, OpenViduRole role, String serverMetadata,
KurentoTokenOptions kurentoTokenOptions) throws OpenViduException { KurentoTokenOptions kurentoTokenOptions) throws OpenViduException {
if (!formatChecker.isServerMetadataFormatCorrect(serverMetadata)) {
Map<String, Token> tokenMap = this.sessionidTokenTokenobj.get(sessionId); log.error("Data invalid format");
throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format");
if (tokenMap != null) {
if (!formatChecker.isServerMetadataFormatCorrect(serverMetadata)) {
log.error("Data invalid format");
throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format");
}
Token token = tokenGenerator.generateToken(sessionId, role, serverMetadata, kurentoTokenOptions);
tokenMap.putIfAbsent(token.getToken(), token);
showTokens();
return token.getToken();
} else {
log.error("sessionId [" + sessionId + "] was not found (race condition error)");
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "sessionId [" + sessionId + "] not found");
} }
Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), role, serverMetadata,
kurentoTokenOptions);
session.storeToken(tokenObj);
session.showTokens("Token created");
return tokenObj.getToken();
} }
public boolean isTokenValidInSession(String token, String sessionId, String participanPrivatetId, public Token newTokenForInsecureUser(Session session, String token, String serverMetadata) {
String serverMetadata) { Token tokenObj = new Token(token, OpenViduRole.PUBLISHER, serverMetadata != null ? serverMetadata : "",
if (!this.isInsecureParticipant(participanPrivatetId)) { this.coturnCredentialsService.isCoturnAvailable() ? this.coturnCredentialsService.createUser() : null,
if (this.sessionidTokenTokenobj.get(sessionId) != null) { null);
return this.sessionidTokenTokenobj.get(sessionId).containsKey(token); session.storeToken(tokenObj);
} else { session.showTokens("Token created for insecure user");
return false; return tokenObj;
}
} else {
this.initializeCollections(sessionId);
this.sessionidTokenTokenobj.get(sessionId).putIfAbsent(token,
new Token(token, OpenViduRole.PUBLISHER, serverMetadata,
this.coturnCredentialsService.isCoturnAvailable()
? this.coturnCredentialsService.createUser()
: null,
null));
return true;
}
} }
public boolean isPublisherInSession(String sessionId, Participant participant) { public boolean isPublisherInSession(String sessionId, Participant participant) {
@ -416,23 +393,6 @@ public abstract class SessionManager {
} }
} }
public Token consumeToken(String sessionId, String participantPrivateId, String token) {
if (this.sessionidTokenTokenobj.get(sessionId) != null) {
Token t = this.sessionidTokenTokenobj.get(sessionId).remove(token);
if (t != null) {
return t;
} else {
throw new OpenViduException(Code.TOKEN_CANNOT_BE_CREATED_ERROR_CODE, sessionId);
}
} else {
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, sessionId);
}
}
public void showTokens() {
log.info("<SESSIONID, TOKENS>: {}", this.sessionidTokenTokenobj.toString());
}
/** /**
* Closes all resources. This method has been annotated with the @PreDestroy * Closes all resources. This method has been annotated with the @PreDestroy
* directive (javax.annotation package) so that it will be automatically called * directive (javax.annotation package) so that it will be automatically called
@ -531,13 +491,11 @@ public abstract class SessionManager {
sessionidParticipantpublicidParticipant.remove(sessionId); sessionidParticipantpublicidParticipant.remove(sessionId);
sessionidFinalUsers.remove(sessionId); sessionidFinalUsers.remove(sessionId);
sessionidAccumulatedRecordings.remove(sessionId); sessionidAccumulatedRecordings.remove(sessionId);
sessionidTokenTokenobj.remove(sessionId);
} }
private void initializeCollections(String sessionId) { private void initializeCollections(String sessionId) {
this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>()); this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>());
this.sessionidFinalUsers.putIfAbsent(sessionId, new ConcurrentHashMap<>()); this.sessionidFinalUsers.putIfAbsent(sessionId, new ConcurrentHashMap<>());
this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>());
if (this.openviduConfig.isRecordingModuleEnabled()) { if (this.openviduConfig.isRecordingModuleEnabled()) {
this.sessionidAccumulatedRecordings.putIfAbsent(sessionId, new ConcurrentLinkedQueue<>()); this.sessionidAccumulatedRecordings.putIfAbsent(sessionId, new ConcurrentLinkedQueue<>());
} }

View File

@ -138,6 +138,8 @@ public class KurentoSession extends Session {
public boolean close(EndReason reason) { public boolean close(EndReason reason) {
if (!closed) { if (!closed) {
this.tokens.clear();
for (Participant participant : participants.values()) { for (Participant participant : participants.values()) {
((KurentoParticipant) participant).releaseAllFilters(); ((KurentoParticipant) participant).releaseAllFilters();
((KurentoParticipant) participant).close(reason, true, 0); ((KurentoParticipant) participant).close(reason, true, 0);

View File

@ -170,9 +170,6 @@ public class KurentoSessionManager extends SessionManager {
this.coturnCredentialsService.deleteUser(p.getToken().getTurnCredentials().getUsername()); this.coturnCredentialsService.deleteUser(p.getToken().getTurnCredentials().getUsername());
} }
if (sessionidTokenTokenobj.get(sessionId) != null) {
sessionidTokenTokenobj.get(sessionId).remove(p.getToken().getToken());
}
boolean stillParticipant = false; boolean stillParticipant = false;
for (Session s : sessions.values()) { for (Session s : sessions.values()) {
if (s.getParticipantByPrivateId(p.getParticipantPrivateId()) != null) { if (s.getParticipantByPrivateId(p.getParticipantPrivateId()) != null) {
@ -185,8 +182,6 @@ public class KurentoSessionManager extends SessionManager {
} }
} }
showTokens();
// Close Session if no more participants // Close Session if no more participants
Set<Participant> remainingParticipants = null; Set<Participant> remainingParticipants = null;
@ -222,7 +217,6 @@ public class KurentoSessionManager extends SessionManager {
log.info("No more participants in session '{}', removing it and closing it", sessionId); log.info("No more participants in session '{}', removing it and closing it", sessionId);
this.closeSessionAndEmptyCollections(session, reason, true); this.closeSessionAndEmptyCollections(session, reason, true);
sessionClosedByLastParticipant = true; sessionClosedByLastParticipant = true;
showTokens();
} finally { } finally {
session.closingLock.writeLock().unlock(); session.closingLock.writeLock().unlock();
} }
@ -909,10 +903,9 @@ public class KurentoSessionManager extends SessionManager {
this.newInsecureParticipant(rtspConnectionId); this.newInsecureParticipant(rtspConnectionId);
String token = IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() String token = IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase()
+ RandomStringUtils.randomAlphanumeric(15); + RandomStringUtils.randomAlphanumeric(15);
Token tokenObj = null; this.newTokenForInsecureUser(session, token, serverMetadata);
if (this.isTokenValidInSession(token, sessionId, rtspConnectionId, serverMetadata)) { final Token tokenObj = session.consumeToken(token);
tokenObj = this.consumeToken(sessionId, rtspConnectionId, token);
}
Participant ipcamParticipant = this.newIpcamParticipant(sessionId, rtspConnectionId, tokenObj, location, Participant ipcamParticipant = this.newIpcamParticipant(sessionId, rtspConnectionId, tokenObj, location,
mediaOptions.getTypeOfVideo()); mediaOptions.getTypeOfVideo());

View File

@ -494,7 +494,6 @@ public class RecordingManager {
log.info("Closing session {} after automatic stop of recording {}", session.getSessionId(), log.info("Closing session {} after automatic stop of recording {}", session.getSessionId(),
recordingId); recordingId);
sessionManager.closeSessionAndEmptyCollections(session, EndReason.automaticStop, true); sessionManager.closeSessionAndEmptyCollections(session, EndReason.automaticStop, true);
sessionManager.showTokens();
} else { } else {
// There are users connected, but no one is publishing // There are users connected, but no one is publishing
session.closingLock.writeLock().unlock(); // We don't need the lock if session's not closing session.closingLock.writeLock().unlock(); // We don't need the lock if session's not closing
@ -536,7 +535,6 @@ public class RecordingManager {
"Ongoing recording of session {} was explicetly stopped within timeout for automatic recording stop. Closing session", "Ongoing recording of session {} was explicetly stopped within timeout for automatic recording stop. Closing session",
session.getSessionId()); session.getSessionId());
sessionManager.closeSessionAndEmptyCollections(session, reason, false); sessionManager.closeSessionAndEmptyCollections(session, reason, false);
sessionManager.showTokens();
} }
return cancelled; return cancelled;
} finally { } finally {

View File

@ -21,6 +21,7 @@ import java.net.MalformedURLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -165,6 +166,8 @@ public class SessionRestController {
} }
Session sessionNotActive = sessionManager.storeSessionNotActive(sessionId, sessionProperties); Session sessionNotActive = sessionManager.storeSessionNotActive(sessionId, sessionProperties);
log.info("New session {} initialized {}", sessionId, this.sessionManager.getSessionsWithNotActive().stream()
.map(Session::getSessionId).collect(Collectors.toList()).toString());
JsonObject responseJson = new JsonObject(); JsonObject responseJson = new JsonObject();
responseJson.addProperty("id", sessionNotActive.getSessionId()); responseJson.addProperty("id", sessionNotActive.getSessionId());
responseJson.addProperty("createdAt", sessionNotActive.getStartTime()); responseJson.addProperty("createdAt", sessionNotActive.getStartTime());
@ -364,7 +367,7 @@ public class SessionRestController {
// While closing a session tokens can't be generated // While closing a session tokens can't be generated
if (session.closingLock.readLock().tryLock()) { if (session.closingLock.readLock().tryLock()) {
try { try {
String token = sessionManager.newToken(sessionId, role, metadata, kurentoTokenOptions); String token = sessionManager.newToken(session, role, metadata, kurentoTokenOptions);
JsonObject responseJson = new JsonObject(); JsonObject responseJson = new JsonObject();
responseJson.addProperty("id", token); responseJson.addProperty("id", token);

View File

@ -176,6 +176,13 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
String platform = getStringParam(request, ProtocolElements.JOINROOM_PLATFORM_PARAM); String platform = getStringParam(request, ProtocolElements.JOINROOM_PLATFORM_PARAM);
String participantPrivatetId = rpcConnection.getParticipantPrivateId(); String participantPrivatetId = rpcConnection.getParticipantPrivateId();
final io.openvidu.server.core.Session session = sessionManager.getSessionWithNotActive(sessionId);
if (session == null) {
log.error("ERROR: Session {} not found", sessionId);
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE,
"Unable to join session. Session " + sessionId + " cannot be found");
}
InetAddress remoteAddress = null; InetAddress remoteAddress = null;
GeoLocation location = null; GeoLocation location = null;
Object obj = rpcConnection.getSession().getAttributes().get("remoteAddress"); Object obj = rpcConnection.getSession().getAttributes().get("remoteAddress");
@ -234,24 +241,25 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
sessionManager.newInsecureParticipant(participantPrivatetId); sessionManager.newInsecureParticipant(participantPrivatetId);
token = IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() token = IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase()
+ RandomStringUtils.randomAlphanumeric(15); + RandomStringUtils.randomAlphanumeric(15);
sessionManager.newTokenForInsecureUser(session, token, null);
if (recorder) { if (recorder) {
generateRecorderParticipant = true; generateRecorderParticipant = true;
} }
} }
if (sessionManager.isTokenValidInSession(token, sessionId, participantPrivatetId, "")) { Token tokenObj = session.consumeToken(token);
if (tokenObj != null) {
String clientMetadata = getStringParam(request, ProtocolElements.JOINROOM_METADATA_PARAM); String clientMetadata = getStringParam(request, ProtocolElements.JOINROOM_METADATA_PARAM);
if (sessionManager.formatChecker.isServerMetadataFormatCorrect(clientMetadata)) { if (sessionManager.formatChecker.isServerMetadataFormatCorrect(clientMetadata)) {
Token tokenObj = sessionManager.consumeToken(sessionId, participantPrivatetId, token);
Participant participant;
io.openvidu.server.core.Session session = sessionManager.getSessionWithNotActive(sessionId);
// While closing a session users can't join // While closing a session users can't join
if (session.closingLock.readLock().tryLock()) { if (session.closingLock.readLock().tryLock()) {
if (session.isClosed()) {
throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE,
"Unable to join the session. Session " + sessionId + " is closed");
}
try { try {
Participant participant;
if (generateRecorderParticipant) { if (generateRecorderParticipant) {
participant = sessionManager.newRecorderParticipant(sessionId, participantPrivatetId, participant = sessionManager.newRecorderParticipant(sessionId, participantPrivatetId,
tokenObj, clientMetadata); tokenObj, clientMetadata);
@ -276,13 +284,13 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
} }
} else { } else {
log.error("ERROR: Metadata format set in client-side is incorrect"); log.error("ERROR: Metadata format set in client-side is incorrect");
throw new OpenViduException(Code.USER_METADATA_FORMAT_INVALID_ERROR_CODE, throw new OpenViduException(Code.USER_METADATA_FORMAT_INVALID_ERROR_CODE, "Unable to join session "
"Unable to join room. The metadata received from the client-side has an invalid format"); + sessionId + ". The metadata received from the client-side has an invalid format");
} }
} else { } else {
log.error("ERROR: sessionId or token not valid"); log.error("ERROR: token not valid");
throw new OpenViduException(Code.USER_UNAUTHORIZED_ERROR_CODE, throw new OpenViduException(Code.USER_UNAUTHORIZED_ERROR_CODE,
"Unable to join room. The user is not authorized"); "Unable to join session " + sessionId + ". Token " + token + "is not valid");
} }
} }