mirror of https://github.com/OpenVidu/openvidu.git
COTURN credentials lifecycle
parent
4340b535e5
commit
3166f67f0d
|
@ -56,6 +56,14 @@ export class OpenVidu {
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
recorder = false;
|
recorder = false;
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
turnCredentials: RTCIceServer;
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
role: string;
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
|
@ -490,7 +498,7 @@ export class OpenVidu {
|
||||||
onreconnected: this.reconnectedCallback.bind(this)
|
onreconnected: this.reconnectedCallback.bind(this)
|
||||||
},
|
},
|
||||||
rpc: {
|
rpc: {
|
||||||
requestTimeout: 15000,
|
requestTimeout: 10000,
|
||||||
participantJoined: this.session.onParticipantJoined.bind(this.session),
|
participantJoined: this.session.onParticipantJoined.bind(this.session),
|
||||||
participantPublished: this.session.onParticipantPublished.bind(this.session),
|
participantPublished: this.session.onParticipantPublished.bind(this.session),
|
||||||
participantUnpublished: this.session.onParticipantUnpublished.bind(this.session),
|
participantUnpublished: this.session.onParticipantUnpublished.bind(this.session),
|
||||||
|
|
|
@ -934,6 +934,9 @@ export class Session implements EventDispatcher {
|
||||||
this.sessionId = <string>url.searchParams.get('sessionId');
|
this.sessionId = <string>url.searchParams.get('sessionId');
|
||||||
const secret = url.searchParams.get('secret');
|
const secret = url.searchParams.get('secret');
|
||||||
const recorder = url.searchParams.get('recorder');
|
const recorder = url.searchParams.get('recorder');
|
||||||
|
const turnUsername = url.searchParams.get('turnUsername');
|
||||||
|
const turnCredential = url.searchParams.get('turnCredential');
|
||||||
|
const role = url.searchParams.get('role');
|
||||||
|
|
||||||
if (!!secret) {
|
if (!!secret) {
|
||||||
this.openvidu.secret = secret;
|
this.openvidu.secret = secret;
|
||||||
|
@ -941,6 +944,13 @@ export class Session implements EventDispatcher {
|
||||||
if (!!recorder) {
|
if (!!recorder) {
|
||||||
this.openvidu.recorder = true;
|
this.openvidu.recorder = true;
|
||||||
}
|
}
|
||||||
|
if (!!turnUsername && !!turnCredential) {
|
||||||
|
const turnUrl = 'turn:' + url.hostname + ':3478';
|
||||||
|
this.openvidu.turnCredentials = { urls: [turnUrl], username: turnUsername, credential: turnCredential };
|
||||||
|
}
|
||||||
|
if (!!role) {
|
||||||
|
this.openvidu.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
this.openvidu.wsUri = 'wss://' + url.host + '/openvidu';
|
this.openvidu.wsUri = 'wss://' + url.host + '/openvidu';
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,6 +359,14 @@ export class Stream {
|
||||||
this.speechEvent = undefined;
|
this.speechEvent = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
isLocal(): boolean {
|
||||||
|
// inbound options undefined and outbound options defined
|
||||||
|
return (!this.inboundStreamOpts && !!this.outboundStreamOpts);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Private methods */
|
/* Private methods */
|
||||||
|
|
||||||
|
@ -374,7 +382,7 @@ export class Stream {
|
||||||
videoStream: this.mediaStream,
|
videoStream: this.mediaStream,
|
||||||
mediaConstraints: userMediaConstraints,
|
mediaConstraints: userMediaConstraints,
|
||||||
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
|
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
|
||||||
iceServers: this.session.openvidu.advancedConfiguration.iceServers
|
iceServers: this.getIceServersConf()
|
||||||
};
|
};
|
||||||
|
|
||||||
const successCallback = (error, sdpOfferParam, wp) => {
|
const successCallback = (error, sdpOfferParam, wp) => {
|
||||||
|
@ -444,7 +452,8 @@ export class Stream {
|
||||||
offerConstraints);
|
offerConstraints);
|
||||||
const options = {
|
const options = {
|
||||||
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
|
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
|
||||||
mediaConstraints: offerConstraints
|
mediaConstraints: offerConstraints,
|
||||||
|
iceServers: this.getIceServersConf()
|
||||||
};
|
};
|
||||||
|
|
||||||
const successCallback = (error, sdpOfferParam, wp) => {
|
const successCallback = (error, sdpOfferParam, wp) => {
|
||||||
|
@ -525,12 +534,12 @@ export class Stream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private getIceServersConf(): RTCIceServer[] | undefined {
|
||||||
* @hidden
|
return !!this.session.openvidu.advancedConfiguration.iceServers ?
|
||||||
*/
|
this.session.openvidu.advancedConfiguration.iceServers :
|
||||||
isLocal(): boolean {
|
!!this.session.openvidu.turnCredentials ?
|
||||||
// inbound options undefined and outbound options defined
|
[this.session.openvidu.turnCredentials] :
|
||||||
return (!this.inboundStreamOpts && !!this.outboundStreamOpts);
|
undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -26,7 +26,6 @@ public class CommandExecutor {
|
||||||
public static String execCommand(String... command) throws IOException, InterruptedException {
|
public static String execCommand(String... command) throws IOException, InterruptedException {
|
||||||
|
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||||
|
|
||||||
processBuilder.redirectErrorStream(true);
|
processBuilder.redirectErrorStream(true);
|
||||||
|
|
||||||
Process process = processBuilder.start();
|
Process process = processBuilder.start();
|
||||||
|
@ -34,20 +33,15 @@ public class CommandExecutor {
|
||||||
|
|
||||||
try (BufferedReader processOutputReader = new BufferedReader(
|
try (BufferedReader processOutputReader = new BufferedReader(
|
||||||
new InputStreamReader(process.getInputStream()));) {
|
new InputStreamReader(process.getInputStream()));) {
|
||||||
String readLine;
|
|
||||||
|
|
||||||
|
String readLine;
|
||||||
while ((readLine = processOutputReader.readLine()) != null) {
|
while ((readLine = processOutputReader.readLine()) != null) {
|
||||||
processOutput.append(readLine + System.lineSeparator());
|
processOutput.append(readLine + System.lineSeparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
process.waitFor();
|
process.waitFor();
|
||||||
}
|
}
|
||||||
|
|
||||||
return processOutput.toString().trim();
|
return processOutput.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
|
||||||
System.out.println(execCommand("/bin/sh","-c","hostname -i | awk '{print $1}'"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ import com.google.gson.JsonParser;
|
||||||
import io.openvidu.server.cdr.CallDetailRecord;
|
import io.openvidu.server.cdr.CallDetailRecord;
|
||||||
import io.openvidu.server.config.OpenviduConfig;
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
import io.openvidu.server.core.SessionManager;
|
import io.openvidu.server.core.SessionManager;
|
||||||
|
import io.openvidu.server.coturn.CoturnCredentialsService;
|
||||||
|
import io.openvidu.server.coturn.CoturnCredentialsServiceFactory;
|
||||||
import io.openvidu.server.kurento.AutodiscoveryKurentoClientProvider;
|
import io.openvidu.server.kurento.AutodiscoveryKurentoClientProvider;
|
||||||
import io.openvidu.server.kurento.KurentoClientProvider;
|
import io.openvidu.server.kurento.KurentoClientProvider;
|
||||||
import io.openvidu.server.kurento.core.KurentoSessionEventsHandler;
|
import io.openvidu.server.kurento.core.KurentoSessionEventsHandler;
|
||||||
|
@ -144,6 +146,11 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
||||||
return new ComposedRecordingService();
|
return new ComposedRecordingService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CoturnCredentialsService coturnCredentialsService() {
|
||||||
|
return new CoturnCredentialsServiceFactory(openviduConfig()).getCoturnCredentialsService();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerJsonRpcHandlers(JsonRpcHandlerRegistry registry) {
|
public void registerJsonRpcHandlers(JsonRpcHandlerRegistry registry) {
|
||||||
registry.addHandler(rpcHandler().withPingWatchdog(true), "/openvidu");
|
registry.addHandler(rpcHandler().withPingWatchdog(true), "/openvidu");
|
||||||
|
|
|
@ -58,6 +58,9 @@ public class OpenviduConfig {
|
||||||
@Value("#{'${spring.profiles.active:}'.length() > 0 ? '${spring.profiles.active:}'.split(',') : \"default\"}")
|
@Value("#{'${spring.profiles.active:}'.length() > 0 ? '${spring.profiles.active:}'.split(',') : \"default\"}")
|
||||||
private String springProfile;
|
private String springProfile;
|
||||||
|
|
||||||
|
@Value("${coturn.sqlite}")
|
||||||
|
private String coturnSqlite;
|
||||||
|
|
||||||
private String finalUrl;
|
private String finalUrl;
|
||||||
|
|
||||||
public String getOpenViduPublicUrl() {
|
public String getOpenViduPublicUrl() {
|
||||||
|
@ -120,6 +123,10 @@ public class OpenviduConfig {
|
||||||
return springProfile;
|
return springProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getCoturnSqlite() {
|
||||||
|
return coturnSqlite;
|
||||||
|
}
|
||||||
|
|
||||||
public ParticipantRole[] getRolesFromRecordingNotification() {
|
public ParticipantRole[] getRolesFromRecordingNotification() {
|
||||||
ParticipantRole[] roles;
|
ParticipantRole[] roles;
|
||||||
switch (this.openviduRecordingNotification) {
|
switch (this.openviduRecordingNotification) {
|
||||||
|
|
|
@ -40,6 +40,8 @@ import io.openvidu.java.client.SessionProperties;
|
||||||
import io.openvidu.server.OpenViduServer;
|
import io.openvidu.server.OpenViduServer;
|
||||||
import io.openvidu.server.cdr.CallDetailRecord;
|
import io.openvidu.server.cdr.CallDetailRecord;
|
||||||
import io.openvidu.server.config.OpenviduConfig;
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
|
import io.openvidu.server.coturn.CoturnCredentialsService;
|
||||||
|
import io.openvidu.server.coturn.TurnCredentials;
|
||||||
import io.openvidu.server.recording.ComposedRecordingService;
|
import io.openvidu.server.recording.ComposedRecordingService;
|
||||||
|
|
||||||
public abstract class SessionManager {
|
public abstract class SessionManager {
|
||||||
|
@ -58,11 +60,14 @@ public abstract class SessionManager {
|
||||||
@Autowired
|
@Autowired
|
||||||
protected OpenviduConfig openviduConfig;
|
protected OpenviduConfig openviduConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected CoturnCredentialsService coturnCredentialsService;
|
||||||
|
|
||||||
protected ConcurrentMap<String, Session> sessions = new ConcurrentHashMap<>();
|
protected ConcurrentMap<String, Session> sessions = new ConcurrentHashMap<>();
|
||||||
protected ConcurrentMap<String, SessionProperties> sessionProperties = new ConcurrentHashMap<>();
|
protected ConcurrentMap<String, SessionProperties> sessionProperties = new ConcurrentHashMap<>();
|
||||||
protected ConcurrentMap<String, ConcurrentHashMap<String, Token>> sessionidTokenTokenobj = new ConcurrentHashMap<>();
|
|
||||||
protected ConcurrentMap<String, ConcurrentHashMap<String, Participant>> sessionidParticipantpublicidParticipant = new ConcurrentHashMap<>();
|
protected ConcurrentMap<String, ConcurrentHashMap<String, Participant>> sessionidParticipantpublicidParticipant = 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<>();
|
||||||
|
|
||||||
private volatile boolean closed = false;
|
private volatile boolean closed = false;
|
||||||
|
|
||||||
|
@ -93,15 +98,6 @@ public abstract class SessionManager {
|
||||||
public void evictParticipant(String participantPrivateId, String reason) throws OpenViduException {
|
public void evictParticipant(String participantPrivateId, String reason) throws OpenViduException {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a sessionId already exists or not
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public boolean sessionIdExists(String sessionId) {
|
|
||||||
return sessionidTokenTokenobj.containsKey(sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Session given its id
|
* Returns a Session given its id
|
||||||
*
|
*
|
||||||
|
@ -190,30 +186,118 @@ public abstract class SessionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void storeSessionId(String sessionId, SessionProperties sessionProperties) {
|
public void storeSessionId(String sessionId, SessionProperties sessionProperties) {
|
||||||
this.sessionidTokenTokenobj.put(sessionId, new ConcurrentHashMap<>());
|
this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>());
|
||||||
this.sessionidParticipantpublicidParticipant.put(sessionId, new ConcurrentHashMap<>());
|
this.sessionProperties.putIfAbsent(sessionId, sessionProperties);
|
||||||
this.sessionProperties.put(sessionId, sessionProperties);
|
|
||||||
showTokens();
|
showTokens();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String newToken(String sessionId, ParticipantRole role, String serverMetadata) throws OpenViduException {
|
public String newToken(String sessionId, ParticipantRole role, String serverMetadata) throws OpenViduException {
|
||||||
if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null
|
|
||||||
&& this.sessionidTokenTokenobj.get(sessionId) != null) {
|
/*if (!isMetadataFormatCorrect(serverMetadata)) {
|
||||||
if (isMetadataFormatCorrect(serverMetadata)) {
|
log.error("Data invalid format. Max length allowed is 10000 chars");
|
||||||
String token = OpenViduServer.publicUrl + "?sessionId=" + sessionId + "&token=";
|
|
||||||
token += this.generateRandomChain();
|
|
||||||
this.sessionidTokenTokenobj.get(sessionId).put(token, new Token(token, role, serverMetadata));
|
|
||||||
showTokens();
|
|
||||||
return token;
|
|
||||||
} else {
|
|
||||||
throw new OpenViduException(Code.GENERIC_ERROR_CODE,
|
throw new OpenViduException(Code.GENERIC_ERROR_CODE,
|
||||||
"Data invalid format. Max length allowed is 10000 chars");
|
"Data invalid format. Max length allowed is 10000 chars");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
System.out.println("Error: the sessionId [" + sessionId + "] is not valid");
|
String token = OpenViduServer.publicUrl;
|
||||||
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "[" + sessionId + "] is not a valid sessionId");
|
token += "?sessionId=" + sessionId;
|
||||||
|
token += "&token=" + this.generateRandomChain();
|
||||||
|
token += "&role=" + role.name();
|
||||||
|
TurnCredentials turnCredentials = null;
|
||||||
|
if (this.coturnCredentialsService.isCoturnAvailable()) {
|
||||||
|
turnCredentials = coturnCredentialsService.createUser();
|
||||||
|
if (turnCredentials != null) {
|
||||||
|
token += "&turnUsername=" + turnCredentials.getUsername();
|
||||||
|
token += "&turnCredential=" + turnCredentials.getCredential();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Token t = new Token(token, role, serverMetadata, turnCredentials);
|
||||||
|
|
||||||
|
final String finalToken = token;
|
||||||
|
|
||||||
|
ConcurrentHashMap<String, Token> tok = this.sessionidTokenTokenobj.computeIfPresent(sessionId, (key, value) -> {
|
||||||
|
value.putIfAbsent(finalToken, t);
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tok == null) {
|
||||||
|
log.error("sessionId [" + sessionId + "] is not valid");
|
||||||
|
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "sessionId [" + sessionId + "] not found");
|
||||||
|
} else {
|
||||||
|
return tok.get(token).getToken();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
/*if (!isMetadataFormatCorrect(serverMetadata)) {
|
||||||
|
log.error("Data invalid format. Max length allowed is 10000 chars");
|
||||||
|
throw new OpenViduException(Code.GENERIC_ERROR_CODE,
|
||||||
|
"Data invalid format. Max length allowed is 10000 chars");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] tokenArray = {""};
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionidTokenTokenobj.computeIfPresent(sessionId, (key, value) -> {
|
||||||
|
String token = OpenViduServer.publicUrl;
|
||||||
|
token += "?sessionId=" + sessionId;
|
||||||
|
token += "&token=" + this.generateRandomChain();
|
||||||
|
token += "&role=" + role.name();
|
||||||
|
TurnCredentials turnCredentials = null;
|
||||||
|
if (this.coturnCredentialsService.isCoturnAvailable()) {
|
||||||
|
turnCredentials = coturnCredentialsService.createUser();
|
||||||
|
if (turnCredentials != null) {
|
||||||
|
token += "&turnUsername=" + turnCredentials.getUsername();
|
||||||
|
token += "&turnCredential=" + turnCredentials.getCredential();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Token t = new Token(token, role, serverMetadata, turnCredentials);
|
||||||
|
value.putIfAbsent(token, t);
|
||||||
|
tokenArray[0] = token;
|
||||||
|
throw new RuntimeException();
|
||||||
|
});
|
||||||
|
} catch(RuntimeException e) {
|
||||||
|
log.info("Token succesfully created");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenArray[0].isEmpty()) {
|
||||||
|
log.error("sessionId [" + sessionId + "] is not valid");
|
||||||
|
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "sessionId [" + sessionId + "] not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenArray[0];*/
|
||||||
|
|
||||||
|
ConcurrentHashMap<String, Token> map = this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>());
|
||||||
|
if (map != null) {
|
||||||
|
|
||||||
|
if (!isMetadataFormatCorrect(serverMetadata)) {
|
||||||
|
log.error("Data invalid format. Max length allowed is 10000 chars");
|
||||||
|
throw new OpenViduException(Code.GENERIC_ERROR_CODE,
|
||||||
|
"Data invalid format. Max length allowed is 10000 chars");
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = OpenViduServer.publicUrl;
|
||||||
|
token += "?sessionId=" + sessionId;
|
||||||
|
token += "&token=" + this.generateRandomChain();
|
||||||
|
token += "&role=" + role.name();
|
||||||
|
TurnCredentials turnCredentials = null;
|
||||||
|
if (this.coturnCredentialsService.isCoturnAvailable()) {
|
||||||
|
turnCredentials = coturnCredentialsService.createUser();
|
||||||
|
token += "&turnUsername=" + turnCredentials.getUsername();
|
||||||
|
token += "&turnCredential=" + turnCredentials.getCredential();
|
||||||
|
}
|
||||||
|
Token t = new Token(token, role, serverMetadata, turnCredentials);
|
||||||
|
|
||||||
|
map.putIfAbsent(token, t);
|
||||||
|
showTokens();
|
||||||
|
return token;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.sessionidTokenTokenobj.remove(sessionId);
|
||||||
|
log.error("sessionId [" + sessionId + "] is not valid");
|
||||||
|
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "sessionId [" + sessionId + "] not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isTokenValidInSession(String token, String sessionId, String participanPrivatetId) {
|
public boolean isTokenValidInSession(String token, String sessionId, String participanPrivatetId) {
|
||||||
if (!this.isInsecureParticipant(participanPrivatetId)) {
|
if (!this.isInsecureParticipant(participanPrivatetId)) {
|
||||||
|
@ -225,7 +309,9 @@ public abstract class SessionManager {
|
||||||
} else {
|
} else {
|
||||||
this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>());
|
this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>());
|
||||||
this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>());
|
this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>());
|
||||||
this.sessionidTokenTokenobj.get(sessionId).putIfAbsent(token, new Token(token, ParticipantRole.PUBLISHER, ""));
|
this.sessionidTokenTokenobj.get(sessionId).putIfAbsent(token,
|
||||||
|
new Token(token, ParticipantRole.PUBLISHER, "",
|
||||||
|
this.coturnCredentialsService.isCoturnAvailable() ? this.coturnCredentialsService.createUser() : null));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,14 +359,12 @@ public abstract class SessionManager {
|
||||||
String clientMetadata) {
|
String clientMetadata) {
|
||||||
if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) {
|
if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) {
|
||||||
String participantPublicId = this.generateRandomChain();
|
String participantPublicId = this.generateRandomChain();
|
||||||
ConcurrentHashMap<String, Participant> participantpublicidParticipant = this.sessionidParticipantpublicidParticipant
|
|
||||||
.get(sessionId);
|
|
||||||
while (participantpublicidParticipant.containsKey(participantPublicId)) {
|
|
||||||
// Avoid random 'participantpublicid' collisions
|
|
||||||
participantPublicId = this.generateRandomChain();
|
|
||||||
}
|
|
||||||
Participant p = new Participant(participantPrivatetId, participantPublicId, token, clientMetadata);
|
Participant p = new Participant(participantPrivatetId, participantPublicId, token, clientMetadata);
|
||||||
this.sessionidParticipantpublicidParticipant.get(sessionId).put(participantPublicId, p);
|
while (this.sessionidParticipantpublicidParticipant.get(sessionId).putIfAbsent(participantPublicId,
|
||||||
|
p) != null) {
|
||||||
|
participantPublicId = this.generateRandomChain();
|
||||||
|
p.setParticipantPublicId(participantPublicId);
|
||||||
|
}
|
||||||
return p;
|
return p;
|
||||||
} else {
|
} else {
|
||||||
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, sessionId);
|
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, sessionId);
|
||||||
|
|
|
@ -17,20 +17,24 @@
|
||||||
|
|
||||||
package io.openvidu.server.core;
|
package io.openvidu.server.core;
|
||||||
|
|
||||||
|
import io.openvidu.server.coturn.TurnCredentials;
|
||||||
|
|
||||||
public class Token {
|
public class Token {
|
||||||
|
|
||||||
String token;
|
String token;
|
||||||
ParticipantRole role;
|
ParticipantRole role;
|
||||||
String serverMetadata = "";
|
String serverMetadata = "";
|
||||||
|
TurnCredentials turnCredentials;
|
||||||
|
|
||||||
public Token(String token) {
|
public Token(String token) {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token(String token, ParticipantRole role, String serverMetadata) {
|
public Token(String token, ParticipantRole role, String serverMetadata, TurnCredentials turnCredentials) {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.role = role;
|
this.role = role;
|
||||||
this.serverMetadata = serverMetadata;
|
this.serverMetadata = serverMetadata;
|
||||||
|
this.turnCredentials = turnCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
|
@ -45,6 +49,10 @@ public class Token {
|
||||||
return serverMetadata;
|
return serverMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TurnCredentials getTurnCredentials() {
|
||||||
|
return turnCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (this.role != null)
|
if (this.role != null)
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package io.openvidu.server.coturn;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
|
||||||
|
import io.openvidu.server.CommandExecutor;
|
||||||
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
|
|
||||||
|
public class BashCoturnCredentialsService extends CoturnCredentialsService {
|
||||||
|
|
||||||
|
public BashCoturnCredentialsService(OpenviduConfig openviduConfig) {
|
||||||
|
super(openviduConfig);
|
||||||
|
File f = new File(this.openviduConfig.getCoturnSqlite());
|
||||||
|
if (f.exists()) {
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
this.coturnDatabaseLocation = this.openviduConfig.getCoturnSqlite();
|
||||||
|
try {
|
||||||
|
String response = CommandExecutor.execCommand("/bin/sh", "-c", "turnadmin -l -b " + this.coturnDatabaseLocation);
|
||||||
|
if (response.contains("turnadmin: not found")) {
|
||||||
|
// No coturn installed in the host machine
|
||||||
|
log.warn("No COTURN server is installed in the host machine");
|
||||||
|
this.coturnAvailable = false;
|
||||||
|
}
|
||||||
|
log.info("COTURN sqlite database location: " + this.openviduConfig.getCoturnSqlite());
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
log.info("Using COTURN credentials service for BASH environment");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TurnCredentials createUser() {
|
||||||
|
TurnCredentials credentials = null;
|
||||||
|
log.info("Creating COTURN user");
|
||||||
|
String user = RandomStringUtils.randomAlphanumeric(8).toUpperCase();
|
||||||
|
String pass = RandomStringUtils.randomAlphanumeric(8).toLowerCase();
|
||||||
|
String command = "turnadmin -a -b " + this.coturnDatabaseLocation + " -u " + user + " -r openvidu -p " + pass;
|
||||||
|
String users = "";
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
CommandExecutor.execCommand("/bin/sh", "-c", command);
|
||||||
|
users = CommandExecutor.execCommand("/bin/sh", "-c", "turnadmin -l -b " + this.coturnDatabaseLocation);
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
if (users.contains(user + "[openvidu]")) {
|
||||||
|
credentials = new TurnCredentials(user, pass);
|
||||||
|
log.info("COTURN user created: true");
|
||||||
|
} else {
|
||||||
|
log.info("COTURN user created: false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteUser(String user) {
|
||||||
|
boolean userRemoved = false;
|
||||||
|
|
||||||
|
log.info("Deleting COTURN user");
|
||||||
|
String command = "turnadmin -d -b " + this.coturnDatabaseLocation + " -u " + user + " -r openvidu";
|
||||||
|
String users = "";
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
CommandExecutor.execCommand("/bin/sh", "-c", command);
|
||||||
|
users = CommandExecutor.execCommand("/bin/sh", "-c", "turnadmin -l -b " + this.coturnDatabaseLocation);
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
userRemoved = !users.contains(user + "[openvidu]");
|
||||||
|
log.info("COTURN user deleted: " + userRemoved);
|
||||||
|
return userRemoved;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.openvidu.server.coturn;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public abstract class CoturnCredentialsService {
|
||||||
|
|
||||||
|
protected static final Logger log = LoggerFactory.getLogger(CoturnCredentialsService.class);
|
||||||
|
|
||||||
|
protected OpenviduConfig openviduConfig;
|
||||||
|
|
||||||
|
protected String coturnDatabaseLocation;
|
||||||
|
|
||||||
|
protected boolean coturnAvailable = true;
|
||||||
|
|
||||||
|
protected ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
public CoturnCredentialsService(OpenviduConfig openviduConfig) {
|
||||||
|
this.openviduConfig = openviduConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract TurnCredentials createUser();
|
||||||
|
|
||||||
|
public abstract boolean deleteUser(String user);
|
||||||
|
|
||||||
|
public boolean isCoturnAvailable() {
|
||||||
|
return this.coturnAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package io.openvidu.server.coturn;
|
||||||
|
|
||||||
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
|
|
||||||
|
public class CoturnCredentialsServiceFactory {
|
||||||
|
|
||||||
|
OpenviduConfig openviduConfig;
|
||||||
|
|
||||||
|
public CoturnCredentialsServiceFactory(OpenviduConfig openviduConfig) {
|
||||||
|
this.openviduConfig = openviduConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoturnCredentialsService getCoturnCredentialsService() {
|
||||||
|
if (!"docker".equals(openviduConfig.getSpringProfile())) {
|
||||||
|
return new BashCoturnCredentialsService(this.openviduConfig);
|
||||||
|
} else {
|
||||||
|
// TODO: return other options
|
||||||
|
return new BashCoturnCredentialsService(this.openviduConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package io.openvidu.server.coturn;
|
||||||
|
|
||||||
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
|
|
||||||
|
public class DockerCoturnCredentialsService extends CoturnCredentialsService {
|
||||||
|
|
||||||
|
public DockerCoturnCredentialsService(OpenviduConfig openviduConfig) {
|
||||||
|
super(openviduConfig);
|
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TurnCredentials createUser() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteUser(String user) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package io.openvidu.server.coturn;
|
||||||
|
|
||||||
|
public class TurnCredentials {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
private String credential;
|
||||||
|
|
||||||
|
public TurnCredentials(String username, String credential) {
|
||||||
|
this.username = username;
|
||||||
|
this.credential = credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCredential() {
|
||||||
|
return credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -104,7 +104,7 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leaveRoom(Participant participant, Integer transactionId, String reason) {
|
public synchronized void leaveRoom(Participant participant, Integer transactionId, String reason) {
|
||||||
log.debug("Request [LEAVE_ROOM] ({})", participant.getParticipantPublicId());
|
log.debug("Request [LEAVE_ROOM] ({})", participant.getParticipantPublicId());
|
||||||
|
|
||||||
KurentoParticipant kParticipant = (KurentoParticipant) participant;
|
KurentoParticipant kParticipant = (KurentoParticipant) participant;
|
||||||
|
@ -124,6 +124,11 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
if (sessionidParticipantpublicidParticipant.get(sessionId) != null) {
|
if (sessionidParticipantpublicidParticipant.get(sessionId) != null) {
|
||||||
Participant p = sessionidParticipantpublicidParticipant.get(sessionId)
|
Participant p = sessionidParticipantpublicidParticipant.get(sessionId)
|
||||||
.remove(participant.getParticipantPublicId());
|
.remove(participant.getParticipantPublicId());
|
||||||
|
|
||||||
|
if (this.coturnCredentialsService.isCoturnAvailable()) {
|
||||||
|
this.coturnCredentialsService.deleteUser(p.getToken().getTurnCredentials().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
if (sessionidTokenTokenobj.get(sessionId) != null) {
|
if (sessionidTokenTokenobj.get(sessionId) != null) {
|
||||||
sessionidTokenTokenobj.get(sessionId).remove(p.getToken().getToken());
|
sessionidTokenTokenobj.get(sessionId).remove(p.getToken().getToken());
|
||||||
}
|
}
|
||||||
|
@ -147,7 +152,7 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
try {
|
try {
|
||||||
remainingParticipants = getParticipants(sessionId);
|
remainingParticipants = getParticipants(sessionId);
|
||||||
} catch (OpenViduException e) {
|
} catch (OpenViduException e) {
|
||||||
log.debug("Possible collision when closing the session '{}' (not found)");
|
log.info("Possible collision when closing the session '{}' (not found)", sessionId);
|
||||||
remainingParticipants = Collections.emptySet();
|
remainingParticipants = Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
import org.json.simple.JSONArray;
|
||||||
import org.json.simple.JSONObject;
|
import org.json.simple.JSONObject;
|
||||||
|
@ -107,6 +108,9 @@ public class SessionRestController {
|
||||||
} else {
|
} else {
|
||||||
builder = builder.mediaMode(MediaMode.ROUTED);
|
builder = builder.mediaMode(MediaMode.ROUTED);
|
||||||
}
|
}
|
||||||
|
if (customSessionId != null && !customSessionId.isEmpty()) {
|
||||||
|
builder = builder.customSessionId(customSessionId);
|
||||||
|
}
|
||||||
builder = builder.defaultCustomLayout((defaultCustomLayout != null) ? defaultCustomLayout : "");
|
builder = builder.defaultCustomLayout((defaultCustomLayout != null) ? defaultCustomLayout : "");
|
||||||
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
@ -120,17 +124,16 @@ public class SessionRestController {
|
||||||
|
|
||||||
String sessionId;
|
String sessionId;
|
||||||
if (customSessionId != null && !customSessionId.isEmpty()) {
|
if (customSessionId != null && !customSessionId.isEmpty()) {
|
||||||
if (sessionManager.sessionIdExists(customSessionId)) {
|
if (sessionManager.sessionidTokenTokenobj.putIfAbsent(customSessionId, new ConcurrentHashMap<>()) != null) {
|
||||||
return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT);
|
return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT);
|
||||||
} else {
|
|
||||||
sessionId = customSessionId;
|
|
||||||
sessionManager.storeSessionId(sessionId, sessionProperties);
|
|
||||||
}
|
}
|
||||||
|
sessionId = customSessionId;
|
||||||
} else {
|
} else {
|
||||||
sessionId = sessionManager.generateRandomChain();
|
sessionId = sessionManager.generateRandomChain();
|
||||||
sessionManager.storeSessionId(sessionId, sessionProperties);
|
sessionManager.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sessionManager.storeSessionId(sessionId, sessionProperties);
|
||||||
JSONObject responseJson = new JSONObject();
|
JSONObject responseJson = new JSONObject();
|
||||||
responseJson.put("id", sessionId);
|
responseJson.put("id", sessionId);
|
||||||
return new ResponseEntity<>(responseJson, HttpStatus.OK);
|
return new ResponseEntity<>(responseJson, HttpStatus.OK);
|
||||||
|
@ -167,9 +170,7 @@ public class SessionRestController {
|
||||||
return this.generateErrorResponse("Role " + params.get("role") + " is not defined", "/api/tokens",
|
return this.generateErrorResponse("Role " + params.get("role") + " is not defined", "/api/tokens",
|
||||||
HttpStatus.BAD_REQUEST);
|
HttpStatus.BAD_REQUEST);
|
||||||
} catch (OpenViduException e) {
|
} catch (OpenViduException e) {
|
||||||
return this.generateErrorResponse(
|
return this.generateErrorResponse(e.getMessage(), "/api/tokens", HttpStatus.BAD_REQUEST);
|
||||||
"Metadata [" + params.get("data") + "] unexpected format. Max length allowed is 10000 chars",
|
|
||||||
"/api/tokens", HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,69 @@
|
||||||
{"properties": [
|
{
|
||||||
|
"properties": [
|
||||||
{
|
{
|
||||||
"name": "kms.uris",
|
"name": "kms.uris",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
"description": "KMS URL's to which OpenVidu Server will try to connect. They are tested in order until a valid one is found"
|
"description": "KMS URL's to which OpenVidu Server will try to connect. They are tested in order until a valid one is found",
|
||||||
|
"defaultValue": "[\"ws://localhost:8888/kurento\"]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.secret",
|
"name": "openvidu.secret",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
"description": "Secret used to connect to OpenVidu Server. This value is required when using the REST API or any server client, as well as when connecting to openvidu-server dashboard"
|
"description": "Secret used to connect to OpenVidu Server. This value is required when using the REST API or any server client, as well as when connecting to openvidu-server dashboard",
|
||||||
|
"defaultValue": "MY_SECRET"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.publicurl",
|
"name": "openvidu.publicurl",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
"description": "URL to connect clients to OpenVidu Server. This must be the full IP of your OpenVidu Server, including protocol, host and port (for example: https://my.openvidu.server.ip:4443). If no port argument is provided, 'server.port' param will be appended to it"
|
"description": "URL to connect clients to OpenVidu Server. This must be the full IP of your OpenVidu Server, including protocol, host and port (for example: https://my.openvidu.server.ip:4443). If no port argument is provided, 'server.port' param will be appended to it",
|
||||||
|
"defaultValue": "local"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.cdr",
|
"name": "openvidu.cdr",
|
||||||
"type": "java.lang.Boolean",
|
"type": "java.lang.Boolean",
|
||||||
"description": "Whether to enable Call Detail Record or not"
|
"description": "Whether to enable Call Detail Record or not",
|
||||||
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.recording",
|
"name": "openvidu.recording",
|
||||||
"type": "java.lang.Boolean",
|
"type": "java.lang.Boolean",
|
||||||
"description": "Whether to start OpenVidu Server with recording module service available or not (a Docker image will be downloaded during the first execution). Apart from setting this param to true, it is also necessary to explicitly configure sessions to be recorded"
|
"description": "Whether to start OpenVidu Server with recording module service available or not (a Docker image will be downloaded during the first execution). Apart from setting this param to true, it is also necessary to explicitly configure sessions to be recorded",
|
||||||
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.recording.path",
|
"name": "openvidu.recording.path",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
"description": "Where to store the recorded video files"
|
"description": "Where to store the recorded video files",
|
||||||
|
"defaultValue": "/opt/openvidu/recordings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.recording.public-access",
|
"name": "openvidu.recording.public-access",
|
||||||
"type": "java.lang.Boolean",
|
"type": "java.lang.Boolean",
|
||||||
"description": "'true' to allow public access to the video files specified in 'openviu.recording.path'. 'false' to only allow access to authenticated users"
|
"description": "'true' to allow public access to the video files specified in 'openviu.recording.path'. 'false' to only allow access to authenticated users",
|
||||||
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.recording.notification",
|
"name": "openvidu.recording.notification",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
"description": "Which users will receive a notfication (client events 'recordingStarted' and 'recordingStopped') when recording starts and stops: 'none', 'publisher_moderator', 'all'"
|
"description": "Which users will receive a notfication (client events 'recordingStarted' and 'recordingStopped') when recording starts and stops: 'none', 'publisher_moderator', 'all'",
|
||||||
|
"defaultValue": "publisher_moderator"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.recording.custom-layout",
|
"name": "openvidu.recording.custom-layout",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
"description": "Where should OpenVidu Server look for custom recording layouts"
|
"description": "Where should OpenVidu Server look for custom recording layouts",
|
||||||
|
"defaultValue": "/opt/openvidu/custom-layout"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openvidu.recording.version",
|
"name": "openvidu.recording.version",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
"description": "Tag for openvidu/openvidu-recording Docker image"
|
"description": "Tag for openvidu/openvidu-recording Docker image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "coturn.sqlite",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Path to COTURN sqlite database to add and remove TURN user credentials",
|
||||||
|
"defaultValue": "/opt/openvidu/coturn/turndb"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]}
|
|
|
@ -9,7 +9,6 @@ server.ssl.key-store: classpath:openvidu-selfsigned.jks
|
||||||
server.ssl.key-store-password: openvidu
|
server.ssl.key-store-password: openvidu
|
||||||
server.ssl.key-store-type: JKS
|
server.ssl.key-store-type: JKS
|
||||||
server.ssl.key-alias: openvidu-selfsigned
|
server.ssl.key-alias: openvidu-selfsigned
|
||||||
kms.uris=[\"ws://localhost:8888/kurento\"]
|
|
||||||
|
|
||||||
openvidu.secret: MY_SECRET
|
openvidu.secret: MY_SECRET
|
||||||
openvidu.publicurl: local
|
openvidu.publicurl: local
|
||||||
|
@ -19,3 +18,7 @@ openvidu.recording.path: /opt/openvidu/recordings
|
||||||
openvidu.recording.public-access: false
|
openvidu.recording.public-access: false
|
||||||
openvidu.recording.notification: publisher_moderator
|
openvidu.recording.notification: publisher_moderator
|
||||||
openvidu.recording.custom-layout: /opt/openvidu/custom-layout
|
openvidu.recording.custom-layout: /opt/openvidu/custom-layout
|
||||||
|
|
||||||
|
kms.uris=[\"ws://localhost:8888/kurento\"]
|
||||||
|
|
||||||
|
coturn.sqlite=/opt/openvidu/coturn/turndb
|
Loading…
Reference in New Issue