mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: External Turn REST API credentials: https://datatracker.ietf.org/doc/html/draft-uberti-rtcweb-turn-rest-00
parent
af5efc4de4
commit
c15d6170da
|
@ -18,14 +18,19 @@
|
|||
package io.openvidu.java.client;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.validator.routines.DomainValidator;
|
||||
import org.apache.commons.validator.routines.InetAddressValidator;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* See
|
||||
|
@ -89,6 +94,7 @@ public class IceServerProperties {
|
|||
private String url;
|
||||
private String username;
|
||||
private String credential;
|
||||
private String staticAuthSecret;
|
||||
|
||||
/**
|
||||
* Set the url for the ICE Server you want to use.
|
||||
|
@ -121,6 +127,23 @@ public class IceServerProperties {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Secret for TURN authentication based on:
|
||||
* - https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
|
||||
* - https://www.ietf.org/proceedings/87/slides/slides-87-behave-10.pdf
|
||||
* This will generate credentials valid for 24 hours which is the recommended value
|
||||
*/
|
||||
public IceServerProperties.Builder staticAuthSecret(String staticAuthSecret) {
|
||||
this.staticAuthSecret = staticAuthSecret;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IceServerProperties.Builder clone() {
|
||||
return new Builder().url(this.url)
|
||||
.username(this.username)
|
||||
.credential(this.credential)
|
||||
.staticAuthSecret(this.staticAuthSecret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link io.openvidu.java.client.RecordingProperties}
|
||||
|
@ -137,6 +160,16 @@ public class IceServerProperties {
|
|||
}
|
||||
this.checkValidStunTurn(this.url);
|
||||
if (this.url.startsWith("turn")) {
|
||||
if (this.staticAuthSecret != null) {
|
||||
if (this.username != null || this.credential != null) {
|
||||
throw new IllegalArgumentException("You can't define username or credential if staticAuthSecret is defined");
|
||||
}
|
||||
try {
|
||||
this.generateTURNCredentials();
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw new IllegalArgumentException("Error while generating credentials: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
if ((this.username == null || this.credential == null)) {
|
||||
throw new IllegalArgumentException("Credentials must be defined while using turn");
|
||||
}
|
||||
|
@ -281,6 +314,35 @@ public class IceServerProperties {
|
|||
this.checkHost(uri, host);
|
||||
this.checkPort(uri, port);
|
||||
}
|
||||
|
||||
|
||||
private void generateTURNCredentials() throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
// 1. Generate random username
|
||||
char[] ALPHANUMERIC =("abcdefghijklmnopqrstuvwxyzABCDEFGHIJK " +
|
||||
"LMNOPQRSTUVWXYZ0123456789").toCharArray();
|
||||
int MAX_LENGTH = 8;
|
||||
StringBuilder randomUsername = new StringBuilder();
|
||||
for(int i =0; i < MAX_LENGTH; i++) {
|
||||
int index = new SecureRandom().nextInt(ALPHANUMERIC.length);
|
||||
randomUsername.append(ALPHANUMERIC[index]);
|
||||
}
|
||||
// 2. Get unix timestamp adding 24 hours to define max credential valid time
|
||||
String unixTimestamp = Long.toString((System.currentTimeMillis() / 1000) + 24*3600);
|
||||
|
||||
// 3. Generate TURN username
|
||||
String username = unixTimestamp + ":" + randomUsername;
|
||||
System.out.println(username);
|
||||
|
||||
// 4. Generate HMAC SHA-1 password
|
||||
SecretKeySpec signingKey = new SecretKeySpec(staticAuthSecret.getBytes(), "HmacSHA1");
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
mac.init(signingKey);
|
||||
String credential = new String(Base64.encodeBase64(mac.doFinal(username.getBytes())));
|
||||
|
||||
// Set credentials in builder
|
||||
this.username = username;
|
||||
this.credential = credential;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -225,7 +225,7 @@ public class OpenviduConfig {
|
|||
|
||||
private boolean webrtcSimulcast = false;
|
||||
|
||||
private List<IceServerProperties> webrtcIceServers;
|
||||
private List<IceServerProperties.Builder> webrtcIceServersBuilders;
|
||||
|
||||
// Plain config properties getters
|
||||
|
||||
|
@ -293,8 +293,8 @@ public class OpenviduConfig {
|
|||
return this.webrtcSimulcast;
|
||||
}
|
||||
|
||||
public List<IceServerProperties> getWebrtcIceServers() {
|
||||
return webrtcIceServers;
|
||||
public List<IceServerProperties.Builder> getWebrtcIceServersBuilders() {
|
||||
return webrtcIceServersBuilders;
|
||||
}
|
||||
|
||||
public String getOpenViduRecordingPath() {
|
||||
|
@ -640,7 +640,7 @@ public class OpenviduConfig {
|
|||
|
||||
checkCertificateType();
|
||||
|
||||
webrtcIceServers = loadWebrtcIceServers("OPENVIDU_WEBRTC_ICE_SERVERS");
|
||||
webrtcIceServersBuilders = loadWebrtcIceServers("OPENVIDU_WEBRTC_ICE_SERVERS");
|
||||
|
||||
}
|
||||
|
||||
|
@ -1170,16 +1170,16 @@ public class OpenviduConfig {
|
|||
}
|
||||
}
|
||||
|
||||
private List<IceServerProperties> loadWebrtcIceServers(String property) {
|
||||
private List<IceServerProperties.Builder> loadWebrtcIceServers(String property) {
|
||||
String rawIceServers = asOptionalString(property);
|
||||
List<IceServerProperties> webrtcIceServers = new ArrayList<>();
|
||||
List<IceServerProperties.Builder> webrtcIceServers = new ArrayList<>();
|
||||
if (rawIceServers == null || rawIceServers.isEmpty()) {
|
||||
return webrtcIceServers;
|
||||
}
|
||||
List<String> arrayIceServers = asJsonStringsArray(property);
|
||||
for (String iceServerString : arrayIceServers) {
|
||||
try {
|
||||
IceServerProperties iceServerProperties = readIceServer(property, iceServerString);
|
||||
IceServerProperties.Builder iceServerProperties = readIceServer(property, iceServerString);
|
||||
webrtcIceServers.add(iceServerProperties);
|
||||
} catch (Exception e) {
|
||||
addError(property, iceServerString + " is not a valid webrtc ice server: " + e.getMessage());
|
||||
|
@ -1188,8 +1188,8 @@ public class OpenviduConfig {
|
|||
return webrtcIceServers;
|
||||
}
|
||||
|
||||
private IceServerProperties readIceServer(String property, String iceServerString) {
|
||||
String url = null, username = null, credential = null;
|
||||
private IceServerProperties.Builder readIceServer(String property, String iceServerString) {
|
||||
String url = null, username = null, credential = null, staticAuthSecret = null;
|
||||
String[] iceServerPropList = iceServerString.split(",");
|
||||
for (String iceServerProp: iceServerPropList) {
|
||||
if (iceServerProp.startsWith("url=")) {
|
||||
|
@ -1198,13 +1198,29 @@ public class OpenviduConfig {
|
|||
username = StringUtils.substringAfter(iceServerProp, "username=");
|
||||
} else if (iceServerProp.startsWith("credential=")) {
|
||||
credential = StringUtils.substringAfter(iceServerProp, "credential=");
|
||||
} else if (iceServerProp.startsWith("staticAuthSecret=")) {
|
||||
staticAuthSecret = StringUtils.substringAfter(iceServerProp, "staticAuthSecret=");
|
||||
} else {
|
||||
addError(property, "Wrong parameter: " + iceServerProp);
|
||||
}
|
||||
}
|
||||
IceServerProperties iceServerProperties = new IceServerProperties.Builder()
|
||||
.url(url).username(username).credential(credential).build();
|
||||
return iceServerProperties;
|
||||
IceServerProperties.Builder builder = new IceServerProperties.Builder().url(url);
|
||||
IceServerProperties.Builder builderCheck = new IceServerProperties.Builder().url(url);
|
||||
if (staticAuthSecret != null) {
|
||||
builder.staticAuthSecret(staticAuthSecret);
|
||||
builderCheck.staticAuthSecret(staticAuthSecret);
|
||||
}
|
||||
if (username != null) {
|
||||
builder.username(username);
|
||||
builderCheck.username(username);
|
||||
}
|
||||
if (credential != null) {
|
||||
builder.credential(credential);
|
||||
builderCheck.credential(credential);
|
||||
}
|
||||
// Validate config input
|
||||
builderCheck.build();
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -922,6 +922,9 @@ public class SessionRestController {
|
|||
JsonObject customIceServerJson = customIceServersJsonArray.get(i).getAsJsonObject();
|
||||
IceServerProperties.Builder iceServerPropertiesBuilder = new IceServerProperties.Builder();
|
||||
iceServerPropertiesBuilder.url(customIceServerJson.get("url").getAsString());
|
||||
if (customIceServerJson.has("staticAuthSecret")) {
|
||||
iceServerPropertiesBuilder.staticAuthSecret(customIceServerJson.get("staticAuthSecret").getAsString());
|
||||
}
|
||||
if (customIceServerJson.has("username")) {
|
||||
iceServerPropertiesBuilder.username(customIceServerJson.get("username").getAsString());
|
||||
}
|
||||
|
@ -934,10 +937,11 @@ public class SessionRestController {
|
|||
} catch (Exception e) {
|
||||
throw new Exception("Type error in some parameter of 'customIceServers': " + e.getMessage());
|
||||
}
|
||||
} else if(!openviduConfig.getWebrtcIceServers().isEmpty()){
|
||||
} else if(!openviduConfig.getWebrtcIceServersBuilders().isEmpty()){
|
||||
// If not defined in connection, check if defined in openvidu config
|
||||
for (IceServerProperties iceServerProperties: openviduConfig.getWebrtcIceServers()) {
|
||||
builder.addCustomIceServer(iceServerProperties);
|
||||
for (IceServerProperties.Builder iceServerPropertiesBuilder: openviduConfig.getWebrtcIceServersBuilders()) {
|
||||
IceServerProperties.Builder configIceBuilder = iceServerPropertiesBuilder.clone();
|
||||
builder.addCustomIceServer(configIceBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue