Substantial openvidu-server refactoring to support metadata in users

pull/20/head
pabloFuente 2017-05-12 19:44:32 +02:00
parent 80216ffe30
commit 1ee067adc2
19 changed files with 393 additions and 266 deletions

View File

@ -262,7 +262,7 @@ For secret "MY_SECRET", the final header would be
| **Operation** | POST | | **Operation** | POST |
| **URL** | https://[YOUR_OPENVIDUSERVER_IP]/newToken | | **URL** | https://[YOUR_OPENVIDUSERVER_IP]/newToken |
| **Headers** | Authorization:Basic _EncodeBase64(OPENVIDUAPP:[YOUR_SECRET])_<br/>Content-Type:application/json | | **Headers** | Authorization:Basic _EncodeBase64(OPENVIDUAPP:[YOUR_SECRET])_<br/>Content-Type:application/json |
| **Body** | {"0": "SESSIONID", "1": "ROLE"} | | **Body** | {"0": "SESSIONID", "1": "ROLE", "2": "METADATA"} |
| **Returns** | {"0": "TOKEN"} | | **Returns** | {"0": "TOKEN"} |
@ -279,6 +279,9 @@ For secret "MY_SECRET", the final header would be
API reference API reference
=================== ===================
> NOTE: all input parameters ("Parameters" columns) are listed in strict order, optional ones in _italics_
## openvidu-browser ## openvidu-browser
| Class | Description | | Class | Description |
@ -290,7 +293,7 @@ API reference
| Stream | Represents each of the videos send and receive by a user in a session. Therefore each Publisher and Subscriber has an attribute of type Stream | | Stream | Represents each of the videos send and receive by a user in a session. Therefore each Publisher and Subscriber has an attribute of type Stream |
#### **OpenVidu** #### **OpenVidu**
| Method | Returns | Parameters (show in order, optional _italic_) | Description | | Method | Returns | Parameters | Description |
| ---------------- | ------- | ------------------------------------------- | ----------- | | ---------------- | ------- | ------------------------------------------- | ----------- |
| `initSession` | Session | _`apikey:string`_<br/>`sessionId:string` | Returns a session with id **sessionId** | | `initSession` | Session | _`apikey:string`_<br/>`sessionId:string` | Returns a session with id **sessionId** |
| `initPublisher` | Publisher | `parentId:string`<br/>`cameraOptions:any`<br/>_`callback:function`_ | Starts local video stream, appending it to **parentId** HTML element, with the specific **cameraOptions** settings and executing **callback** function in the end | | `initPublisher` | Publisher | `parentId:string`<br/>`cameraOptions:any`<br/>_`callback:function`_ | Starts local video stream, appending it to **parentId** HTML element, with the specific **cameraOptions** settings and executing **callback** function in the end |
@ -298,12 +301,12 @@ API reference
| `getDevices` | Promise | `callback(error, deviceInfo):function` | Collects information about the media input and output devices available on the system, returned in **deviceInfo** array | | `getDevices` | Promise | `callback(error, deviceInfo):function` | Collects information about the media input and output devices available on the system, returned in **deviceInfo** array |
#### **Session** #### **Session**
| Method | Returns | Parameters (show in order, optional _italic_) | Description | | Method | Returns | Parameters | Description |
| ---------------- | ------- | ------------------------------------------- | ----------- | | ---------------- | ------- | ------------------------------------------- | ----------- |
| `connect` | | `token:string`<br/>`callback(error):function` | Connects to the session using **token** and executes **callback** in the end (_error_ parameter null if success)| | `connect` | | `token:string`<br/>_`metadata:string`_<br/>`callback(error):function` | Connects to the session using **token** and executes **callback** in the end (_error_ parameter null if success). **metadata** parameter allows you to pass a string as extra data to share with other users when they receive _participantJoined_ event. You can also add metadata through openvidu-backend-client when generating tokens (see [TokenOptions](#tokenoptions)). The structure of this string is up to you (maybe some standarized format as JSON or XML is a good idea), the only restriction is a maximum length of 1000 chars |
| `disconnect` | | | Leaves the session, destroying all streams and deleting the user as a participant | | `disconnect` | | | Leaves the session, destroying all streams and deleting the user as a participant |
| `publish` | | `publisher:Publisher` | Publishes the specific user's local stream contained in Publisher object to the session | | `publish` | | `publisher:Publisher` | Publishes the specific user's local stream contained in **publisher** object to the session |
| `unpublish` | | `publisher:Publisher` | Unpublishes the specific user's local stream contained in Publisher object | | `unpublish` | | `publisher:Publisher` | Unpublishes the specific user's local stream contained in **publisher** object |
| `on` | | `eventName:string`<br/>`callback:function` | **callback** function will be triggered each time **eventName** event is recieved | | `on` | | `eventName:string`<br/>`callback:function` | **callback** function will be triggered each time **eventName** event is recieved |
| `once` | | `eventName:string`<br/>`callback:function` | **callback** function will be triggered once when **eventName** event is recieved. The listener is removed immediately | | `once` | | `eventName:string`<br/>`callback:function` | **callback** function will be triggered once when **eventName** event is recieved. The listener is removed immediately |
| `off` | | `eventName:string`<br/>`eventHandler:any` | Removes **eventHandler** handler for **eventName** event | | `off` | | `eventName:string`<br/>`eventHandler:any` | Removes **eventHandler** handler for **eventName** event |
@ -316,7 +319,7 @@ API reference
#### **Publisher** #### **Publisher**
| Method | Returns | Parameters (show in order, optional _italic_) | Description | | Method | Returns | Parameters | Description |
| -------------- | ------- | ------------------------------------------- | ----------- | | -------------- | ------- | ------------------------------------------- | ----------- |
| `publishAudio` | | `value:boolean`| Enable or disable the audio track depending on whether value is _true_ or _false_ | | `publishAudio` | | `value:boolean`| Enable or disable the audio track depending on whether value is _true_ or _false_ |
| `publishVideo` | | `value:boolean`| Enable or disable the video track depending on whether value is _true_ or _false_ | | `publishVideo` | | `value:boolean`| Enable or disable the video track depending on whether value is _true_ or _false_ |
@ -331,7 +334,7 @@ API reference
| `session` | Session | The session to which the publisher belongs | | `session` | Session | The session to which the publisher belongs |
#### **Subscriber** #### **Subscriber**
| Method | Returns | Parameters (show in order, optional _italic_) | Description | | Method | Returns | Parameters | Description |
| -------------- | ------- | ------------------------------------------- | ----------- | | -------------- | ------- | ------------------------------------------- | ----------- |
| | | | | | | | | |
@ -344,23 +347,45 @@ API reference
## openvidu-backend-client ## openvidu-backend-client
| Class | Description | | Class | Description |
| -------- | ------------------------------------------------------- | | ------------ | ------------------------------------------------------- |
| OpenVidu | Use it to create all the sessions you need | | OpenVidu | Use it to create all the sessions you need |
| Session | Allows for the creation of tokens with different roles | | Session | Allows for the creation of tokens |
| OpenViduRole | Enum that defines the values accepted by _TokenOptions.Builder.role(OpenViduRole role)_ method |
| TokenOptions | Customize each token with this class when generating them |
#### **OpenVidu** #### **OpenVidu**
| Method | Returns | Parameters (show in order, optional _italic_) | Description | | Method | Returns | Parameters | Description |
| -------------- | ------- | --------------------------------------------- | ----------- | | -------------- | ------- | --------------------------------------------- | ----------- |
| OpenVidu() | | `String urlOpenViduServer`<br>`String secret` | The constructor receives the URL of your OpenVidu Server and the secret shared with it | | OpenVidu() | | `String:urlOpenViduServer`<br>`String:secret` | The constructor receives the URL of your OpenVidu Server and the secret shared with it |
| createSession() | Session | | Get a Session object by calling this method. You can then store it as you want | | createSession() | Session | | Get a Session object by calling this method. You can then store it as you want |
#### **Session** #### **Session**
| Method | Returns | Parameters (show in order, optional _italic_) | Description | | Method | Returns | Parameters | Description |
| -------------- | ------- | --------------------------------------------- | ----------- | | -------------- | ------- | --------------------------------------------- | ----------- |
| getSessionId() | String | | Returns the unique identifier of the session. You will need to return this parameter to the client side to pass it during the connection process to the session | | getSessionId() | String | | Returns the unique identifier of the session. You will need to return this parameter to the client side to pass it during the connection process to the session |
| generateToken() | String | _OpenViduRole_ | You can choose which role each user has in a certain session. The value returned is required in the client side just as the sessionId in order to connect to a session. The input parameter can be _OpenViduRole.SUBSCRIBER_, _OpenViduRole.PUBLISHER_ or _OpenViduRole.MODERATOR_. The default value if it is void is _OpenViduRole.PUBLISHER_ | | generateToken() | String | _`TokenOptions:tokenOptions`_ | The value returned is required in the client side just as the sessionId in order to connect to a session |
#### **OpenViduRole**
| Enum | Description |
| ---------- | ------- |
| SUBSCRIBER | They can subscribe to published streams of other users |
| PUBLISHER | They can subscribe to published streams of other users and publish their own streams|
| MODERATOR | They can subscribe to published streams of other users, publish their own streams and force _unpublish()_ and _disconnect()_ over a third-party stream or user |
#### **TokenOptions**
| Method | Returns | Parameters | Description |
| -------------- | ------- | -------------------------------------------| -- |
| getData() | String | | Returns the metadata associated to the token |
| getRole() | OpenViduRole | | Returns the role associated to the token |
##### **TokenOptions.Builder** _(inner static class)_
| Method | Returns | Parameters | Description |
| -------------- | ------- | --------------------------------------------- | ----------- |
| TokenOptions.Builder() | | | Constructor |
| build() | TokenOptions | | Returns a new **TokenOptions** object with the stablished properties. Default values if methods _data()_ and _role()_ are not called are an empty string and OpenViduRole.PUBLISHER, respectively |
| data() | TokenOptions.Builder | `String:data` | Some extra metadata to be associated to the user through its token. The structure of this string is up to you (maybe some standarized format as JSON or XML is a good idea), the only restriction is a maximum length of 1000 chars |
| role() | TokenOptions.Builder | `OpenViduRole:role` | The role associated to this token |
---------- ----------

View File

@ -65,100 +65,4 @@ public class OpenVidu {
return s; return s;
} }
public class Session {
private HttpClient httpClient;
private String urlOpenViduServer;
private String sessionId;
private Session(HttpClient httpClient, String urlOpenViduServer) throws OpenViduException {
this.httpClient = httpClient;
this.urlOpenViduServer = urlOpenViduServer;
this.sessionId = this.getSessionId();
}
public String getSessionId() throws OpenViduException {
if (this.hasSessionId()) {
return this.sessionId;
}
try {
HttpResponse response = httpClient.execute(new HttpGet(this.urlOpenViduServer + "getSessionId"));
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)){
System.out.println("Returning a SESSIONID");
String id = "";
id = this.httpResponseToString(response);
this.sessionId = id;
return id;
} else {
throw new OpenViduException(Code.SESSIONID_CANNOT_BE_CREATED_ERROR_CODE, Integer.toString(statusCode));
}
} catch (Exception e) {
throw new OpenViduException(Code.SESSIONID_CANNOT_BE_CREATED_ERROR_CODE, "Unable to generate a sessionID: " + e.getMessage());
}
}
public String generateToken() throws OpenViduException {
return this.generateToken(OpenViduRole.PUBLISHER);
}
public String generateToken(OpenViduRole role) throws OpenViduException {
if (!this.hasSessionId()){
this.getSessionId();
}
try {
JSONObject json = new JSONObject();
json.put(0, this.sessionId);
json.put(1, role.name());
HttpPost request = new HttpPost(this.urlOpenViduServer + "newToken");
StringEntity params = new StringEntity(json.toString());
request.addHeader("content-type", "application/json");
request.setEntity(params);
HttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)){
System.out.println("Returning a TOKEN");
return this.httpResponseToString(response);
} else {
throw new OpenViduException(Code.TOKEN_CANNOT_BE_CREATED_ERROR_CODE, Integer.toString(statusCode));
}
} catch (Exception e) {
throw new OpenViduException(Code.TOKEN_CANNOT_BE_CREATED_ERROR_CODE, "Unable to generate a token: " + e.getMessage());
}
}
@Override
public String toString() {
return this.sessionId;
}
private String httpResponseToString(HttpResponse response) throws IOException, ParseException{
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer buf = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
buf.append(line);
}
JSONParser parser = new JSONParser();
return ((String) ((JSONObject) parser.parse(buf.toString())).get("0"));
}
private boolean hasSessionId() {
return (this.sessionId != null && !this.sessionId.isEmpty());
}
}
} }

View File

@ -39,7 +39,9 @@ public class OpenViduException extends RuntimeException {
103), USER_NOT_FOUND_ERROR_CODE(102), USER_GENERIC_ERROR_CODE(101), 103), USER_NOT_FOUND_ERROR_CODE(102), USER_GENERIC_ERROR_CODE(101),
USER_UNAUTHORIZED_ERROR_CODE(401), ROLE_NOT_FOUND_ERROR_CODE(402), USER_UNAUTHORIZED_ERROR_CODE(401), ROLE_NOT_FOUND_ERROR_CODE(402),
SESSIONID_CANNOT_BE_CREATED_ERROR_CODE(403), TOKEN_CANNOT_BE_CREATED_ERROR_CODE(404); SESSIONID_CANNOT_BE_CREATED_ERROR_CODE(403), TOKEN_CANNOT_BE_CREATED_ERROR_CODE(404),
USER_METADATA_FORMAT_INVALID_ERROR_CODE(500);
private int value; private int value;

View File

@ -0,0 +1,110 @@
package org.openvidu.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.openvidu.client.OpenViduException.Code;
public class Session {
private HttpClient httpClient;
private String urlOpenViduServer;
private String sessionId;
protected Session(HttpClient httpClient, String urlOpenViduServer) throws OpenViduException {
this.httpClient = httpClient;
this.urlOpenViduServer = urlOpenViduServer;
this.sessionId = this.getSessionId();
}
public String getSessionId() throws OpenViduException {
if (this.hasSessionId()) {
return this.sessionId;
}
try {
HttpResponse response = httpClient.execute(new HttpGet(this.urlOpenViduServer + "getSessionId"));
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)){
System.out.println("Returning a SESSIONID");
String id = "";
id = this.httpResponseToString(response);
this.sessionId = id;
return id;
} else {
throw new OpenViduException(Code.SESSIONID_CANNOT_BE_CREATED_ERROR_CODE, Integer.toString(statusCode));
}
} catch (Exception e) {
throw new OpenViduException(Code.SESSIONID_CANNOT_BE_CREATED_ERROR_CODE, "Unable to generate a sessionID: " + e.getMessage());
}
}
public String generateToken() throws OpenViduException {
return this.generateToken(new TokenOptions.Builder().role(OpenViduRole.PUBLISHER).build());
}
public String generateToken(TokenOptions tokenOptions) throws OpenViduException {
if (!this.hasSessionId()){
this.getSessionId();
}
try {
JSONObject json = new JSONObject();
json.put(0, this.sessionId);
json.put(1, tokenOptions.getRole().name());
json.put(2, tokenOptions.getData());
HttpPost request = new HttpPost(this.urlOpenViduServer + "newToken");
StringEntity params = new StringEntity(json.toString());
request.addHeader("content-type", "application/json");
request.setEntity(params);
HttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)){
System.out.println("Returning a TOKEN");
return this.httpResponseToString(response);
} else {
throw new OpenViduException(Code.TOKEN_CANNOT_BE_CREATED_ERROR_CODE, Integer.toString(statusCode));
}
} catch (Exception e) {
throw new OpenViduException(Code.TOKEN_CANNOT_BE_CREATED_ERROR_CODE, "Unable to generate a token: " + e.getMessage());
}
}
@Override
public String toString() {
return this.sessionId;
}
private String httpResponseToString(HttpResponse response) throws IOException, ParseException{
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer buf = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
buf.append(line);
}
JSONParser parser = new JSONParser();
return ((String) ((JSONObject) parser.parse(buf.toString())).get("0"));
}
private boolean hasSessionId() {
return (this.sessionId != null && !this.sessionId.isEmpty());
}
}

View File

@ -0,0 +1,42 @@
package org.openvidu.client;
public class TokenOptions {
private String data;
private OpenViduRole role;
public static class Builder {
private String data = "";
private OpenViduRole role = OpenViduRole.PUBLISHER;
public TokenOptions build() {
return new TokenOptions(this.data, this.role);
}
public Builder data(String data){
this.data = data;
return this;
}
public Builder role(OpenViduRole role){
this.role = role;
return this;
}
}
public TokenOptions(String data, OpenViduRole role){
this.data = data;
this.role = role;
}
public String getData() {
return this.data;
}
public OpenViduRole getRole() {
return this.role;
}
}

File diff suppressed because one or more lines are too long

View File

@ -18,14 +18,28 @@ export class Session {
}); });
} }
connect(token, callback) { connect(token: string, callback: any);
connect(token:string, metadata: string, callback: any);
connect(param1, param2, param3?) {
// Early configuration to deactivate automatic subscription to streams // Early configuration to deactivate automatic subscription to streams
this.session.configure({ if (typeof param2 == "string") {
sessionId: this.session.getSessionId(), this.session.configure({
participantId: token, sessionId: this.session.getSessionId(),
subscribeToStreams: false participantId: param1,
}); metadata: param2,
this.session.connect(token, callback); subscribeToStreams: false
});
this.session.connect(param1, param3);
} else {
this.session.configure({
sessionId: this.session.getSessionId(),
participantId: param1,
metadata: '',
subscribeToStreams: false
});
this.session.connect(param1, param2);
}
} }
disconnect() { disconnect() {

View File

@ -46,8 +46,9 @@ export class SessionInternal {
}); });
let joinParams = { let joinParams = {
user: token, token: token,
room: this.sessionId, session: this.sessionId,
metadata: '',
dataChannels: false dataChannels: false
} }
@ -111,60 +112,6 @@ export class SessionInternal {
publish() { publish() {
this.openVidu.getCamera().publish(); this.openVidu.getCamera().publish();
} }
onStreamAddedOV(callback) {
this.addEventListener("stream-added", streamEvent => {
callback(streamEvent.stream);
});
}
onStreamRemovedOV(callback) {
this.addEventListener("stream-removed", streamEvent => {
callback(streamEvent.stream);
});
}
onParticipantJoinedOV(callback) {
this.addEventListener("participant-joined", participantEvent => {
callback(participantEvent.participant);
});
}
onParticipantLeftOV(callback) {
this.addEventListener("participant-left", participantEvent => {
callback(participantEvent.participant);
});
}
onParticipantPublishedOV(callback) {
this.addEventListener("participant-published", participantEvent => {
callback(participantEvent.participant);
});
}
onParticipantEvictedOV(callback) {
this.addEventListener("participant-evicted", participantEvent => {
callback(participantEvent.participant);
});
}
onRoomClosedOV(callback) {
this.addEventListener("room-closed", roomEvent => {
callback(roomEvent.room);
});
}
onLostConnectionOV(callback) {
this.addEventListener("lost-connection", roomEvent => {
callback(roomEvent.room);
});
}
onMediaErrorOV(callback) {
this.addEventListener("error-media", errorEvent => {
callback(errorEvent.error)
});
}
/* NEW METHODS */ /* NEW METHODS */

View File

@ -39,7 +39,9 @@ public class OpenViduException extends RuntimeException {
103), USER_NOT_FOUND_ERROR_CODE(102), USER_GENERIC_ERROR_CODE(101), 103), USER_NOT_FOUND_ERROR_CODE(102), USER_GENERIC_ERROR_CODE(101),
USER_UNAUTHORIZED_ERROR_CODE(401), ROLE_NOT_FOUND_ERROR_CODE(402), USER_UNAUTHORIZED_ERROR_CODE(401), ROLE_NOT_FOUND_ERROR_CODE(402),
SESSIONID_CANNOT_BE_CREATED_ERROR_CODE(403), TOKEN_CANNOT_BE_CREATED_ERROR_CODE(404); SESSIONID_CANNOT_BE_CREATED_ERROR_CODE(403), TOKEN_CANNOT_BE_CREATED_ERROR_CODE(404),
USER_METADATA_FORMAT_INVALID_ERROR_CODE(500);
private int value; private int value;

View File

@ -33,8 +33,9 @@ public class ProtocolElements {
public static final String LEAVEROOM_METHOD = "leaveRoom"; public static final String LEAVEROOM_METHOD = "leaveRoom";
public static final String JOINROOM_METHOD = "joinRoom"; public static final String JOINROOM_METHOD = "joinRoom";
public static final String JOINROOM_USER_PARAM = "user"; public static final String JOINROOM_USER_PARAM = "token";
public static final String JOINROOM_ROOM_PARAM = "room"; public static final String JOINROOM_ROOM_PARAM = "session";
public static final String JOINROOM_METADATA_PARAM = "metadata";
public static final String JOINROOM_DATACHANNELS_PARAM = "dataChannels"; public static final String JOINROOM_DATACHANNELS_PARAM = "dataChannels";
public static final String JOINROOM_PEERID_PARAM = "id"; public static final String JOINROOM_PEERID_PARAM = "id";
public static final String JOINROOM_PEERSTREAMS_PARAM = "streams"; public static final String JOINROOM_PEERSTREAMS_PARAM = "streams";
@ -67,6 +68,7 @@ public class ProtocolElements {
public static final String PARTICIPANTJOINED_METHOD = "participantJoined"; public static final String PARTICIPANTJOINED_METHOD = "participantJoined";
public static final String PARTICIPANTJOINED_USER_PARAM = "id"; public static final String PARTICIPANTJOINED_USER_PARAM = "id";
public static final String PARTICIPANTJOINED_METADATA_PARAM = "metadata";
public static final String PARTICIPANTLEFT_METHOD = "participantLeft"; public static final String PARTICIPANTLEFT_METHOD = "participantLeft";
public static final String PARTICIPANTLEFT_NAME_PARAM = "name"; public static final String PARTICIPANTLEFT_NAME_PARAM = "name";

View File

@ -7,7 +7,8 @@ import java.util.concurrent.ConcurrentHashMap;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.openvidu.client.OpenVidu; import org.openvidu.client.OpenVidu;
import org.openvidu.client.OpenVidu.Session; import org.openvidu.client.Session;
import org.openvidu.client.TokenOptions;
import org.openvidu.client.OpenViduRole; import org.openvidu.client.OpenViduRole;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -80,9 +81,10 @@ public class SessionController {
} }
else { else {
try { try {
// IMPORTANT STUFF
Session session = this.openVidu.createSession(); Session session = this.openVidu.createSession();
String sessionId = session.getSessionId(); String sessionId = session.getSessionId();
// END IMPORTANT STUFF
this.lessonIdSession.put(id_lesson, session); this.lessonIdSession.put(id_lesson, session);
this.sessionIdUserIdToken.put(sessionId, new HashMap<>()); this.sessionIdUserIdToken.put(sessionId, new HashMap<>());
@ -130,7 +132,14 @@ public class SessionController {
JSONObject responseJson = new JSONObject(); JSONObject responseJson = new JSONObject();
try { try {
String token = (String) this.lessonIdSession.get(id_lesson).generateToken(role); // IMPORTANT STUFF
TokenOptions tokenOpts = new TokenOptions.Builder()
.role(role)
.data("mydata=mydata")
.build();
String token = (String) this.lessonIdSession.get(id_lesson).generateToken(tokenOpts);
// END IMPORTANT STUFF
this.sessionIdUserIdToken.get(session.getSessionId()).put(this.user.getLoggedUser().getId(), token); this.sessionIdUserIdToken.get(session.getSessionId()).put(this.user.getLoggedUser().getId(), token);
responseJson.put(0, session.getSessionId()); responseJson.put(0, session.getSessionId());

View File

@ -76,22 +76,22 @@ public class NotificationRoomManager {
* when responding back to the client) * when responding back to the client)
* @see RoomManager#joinRoom(String, String, boolean, boolean, KurentoClientSessionInfo, String) * @see RoomManager#joinRoom(String, String, boolean, boolean, KurentoClientSessionInfo, String)
*/ */
public void joinRoom(String userName, String roomName, boolean dataChannels, public void joinRoom(String user, String session, boolean dataChannels,
boolean webParticipant, ParticipantRequest request) { boolean webParticipant, ParticipantRequest request) {
Set<UserParticipant> existingParticipants = null; Set<UserParticipant> existingParticipants = null;
try { try {
KurentoClientSessionInfo kcSessionInfo = KurentoClientSessionInfo kcSessionInfo =
new DefaultKurentoClientSessionInfo(request.getParticipantId(), roomName); new DefaultKurentoClientSessionInfo(request.getParticipantId(), session);
existingParticipants = internalManager existingParticipants = internalManager
.joinRoom(userName, roomName, dataChannels, webParticipant, kcSessionInfo, .joinRoom(user, session, dataChannels, webParticipant, kcSessionInfo,
request.getParticipantId()); request.getParticipantId());
} catch (OpenViduException e) { } catch (OpenViduException e) {
log.warn("PARTICIPANT {}: Error joining/creating room {}", userName, roomName, e); log.warn("PARTICIPANT {}: Error joining/creating room {}", user, session, e);
notificationRoomHandler.onParticipantJoined(request, roomName, userName, null, e); notificationRoomHandler.onParticipantJoined(request, session, user, null, e);
} }
if (existingParticipants != null) { if (existingParticipants != null) {
notificationRoomHandler notificationRoomHandler
.onParticipantJoined(request, roomName, userName, existingParticipants, null); .onParticipantJoined(request, session, user, existingParticipants, null);
} }
} }
@ -431,7 +431,7 @@ public class NotificationRoomManager {
return this.internalManager.newSessionId(); return this.internalManager.newSessionId();
} }
public String newToken(String sessionId, ParticipantRole role){ public String newToken(String sessionId, ParticipantRole role, String metaData){
return this.internalManager.newToken(sessionId, role); return this.internalManager.newToken(sessionId, role, metaData);
} }
} }

View File

@ -45,6 +45,7 @@ import org.openvidu.server.core.endpoint.SdpType;
import org.openvidu.server.core.internal.Participant; import org.openvidu.server.core.internal.Participant;
import org.openvidu.server.core.internal.Room; import org.openvidu.server.core.internal.Room;
import org.openvidu.server.security.ParticipantRole; import org.openvidu.server.security.ParticipantRole;
import org.openvidu.server.security.Token;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -71,7 +72,7 @@ public class RoomManager {
private final ConcurrentMap<String, Room> rooms = new ConcurrentHashMap<String, Room>(); private final ConcurrentMap<String, Room> rooms = new ConcurrentHashMap<String, Room>();
private final ConcurrentMap<String, ConcurrentHashMap<String, ParticipantRole>> sessionIdTokenRole = new ConcurrentHashMap<>(); private final ConcurrentMap<String, ConcurrentHashMap<String, Token>> sessionIdToken = new ConcurrentHashMap<>();
@Value("${openvidu.security}") @Value("${openvidu.security}")
private boolean SECURITY_ENABLED; private boolean SECURITY_ENABLED;
@ -107,30 +108,30 @@ public class RoomManager {
* @return set of existing peers of type {@link UserParticipant}, can be empty if first * @return set of existing peers of type {@link UserParticipant}, can be empty if first
* @throws OpenViduException on error while joining (like the room is not found or is closing) * @throws OpenViduException on error while joining (like the room is not found or is closing)
*/ */
public Set<UserParticipant> joinRoom(String userName, String roomName, boolean dataChannels, public Set<UserParticipant> joinRoom(String user, String session, boolean dataChannels,
boolean webParticipant, KurentoClientSessionInfo kcSessionInfo, String participantId) boolean webParticipant, KurentoClientSessionInfo kcSessionInfo, String participantId)
throws OpenViduException { throws OpenViduException {
log.debug("Request [JOIN_ROOM] user={}, room={}, web={} " + "kcSessionInfo.room={} ({})", log.debug("Request [JOIN_ROOM] user={}, room={}, web={} " + "kcSessionInfo.room={} ({})",
userName, roomName, webParticipant, user, session, webParticipant,
kcSessionInfo != null ? kcSessionInfo.getRoomName() : null, participantId); kcSessionInfo != null ? kcSessionInfo.getRoomName() : null, participantId);
Room room = rooms.get(roomName); Room room = rooms.get(session);
if (room == null && kcSessionInfo != null) { if (room == null && kcSessionInfo != null) {
createRoom(kcSessionInfo); createRoom(kcSessionInfo);
} }
room = rooms.get(roomName); room = rooms.get(session);
if (room == null) { if (room == null) {
log.warn("Room '{}' not found"); log.warn("Room '{}' not found");
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE,
"Room '" + roomName + "' was not found, must be created before '" + userName "Room '" + session + "' was not found, must be created before '" + session
+ "' can join"); + "' can join");
} }
if (room.isClosed()) { if (room.isClosed()) {
log.warn("'{}' is trying to join room '{}' but it is closing", userName, roomName); log.warn("'{}' is trying to join room '{}' but it is closing", user, session);
throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE,
"'" + userName + "' is trying to join room '" + roomName + "' but it is closing"); "'" + user + "' is trying to join room '" + session + "' but it is closing");
} }
Set<UserParticipant> existingParticipants = getParticipants(roomName); Set<UserParticipant> existingParticipants = getParticipants(session);
room.join(participantId, userName, dataChannels, webParticipant); room.join(participantId, user, dataChannels, webParticipant);
return existingParticipants; return existingParticipants;
} }
@ -159,8 +160,8 @@ public class RoomManager {
} }
room.leave(participantId); room.leave(participantId);
if (this.sessionIdTokenRole.get(roomName) != null){ if (this.sessionIdToken.get(roomName) != null){
this.sessionIdTokenRole.get(roomName).remove(participant.getName()); this.sessionIdToken.get(roomName).remove(participant.getName());
} }
showMap(); showMap();
@ -177,7 +178,7 @@ public class RoomManager {
room.close(); room.close();
rooms.remove(roomName); rooms.remove(roomName);
sessionIdTokenRole.remove(roomName); sessionIdToken.remove(roomName);
showMap(); showMap();
@ -848,7 +849,7 @@ public class RoomManager {
room.close(); room.close();
rooms.remove(roomName); rooms.remove(roomName);
sessionIdTokenRole.remove(roomName); sessionIdToken.remove(roomName);
log.warn("Room '{}' removed and closed", roomName); log.warn("Room '{}' removed and closed", roomName);
return participants; return participants;
@ -932,7 +933,7 @@ public class RoomManager {
private void showMap(){ private void showMap(){
System.out.println("------------------------------"); System.out.println("------------------------------");
System.out.println(this.sessionIdTokenRole.toString()); System.out.println(this.sessionIdToken.toString());
System.out.println("------------------------------"); System.out.println("------------------------------");
} }
@ -940,10 +941,10 @@ public class RoomManager {
return getParticipant(pid).getRoom().getName(); return getParticipant(pid).getRoom().getName();
} }
public boolean isParticipantInRoom(String participantName, String roomName) { public boolean isParticipantInRoom(String token, String session) {
if (SECURITY_ENABLED) { if (SECURITY_ENABLED) {
if (this.sessionIdTokenRole.get(roomName) != null) { if (this.sessionIdToken.get(session) != null) {
return this.sessionIdTokenRole.get(roomName).containsKey(participantName); return this.sessionIdToken.get(session).containsKey(token);
} else{ } else{
return false; return false;
} }
@ -952,11 +953,11 @@ public class RoomManager {
} }
} }
public boolean isPublisherInRoom(String participantName, String roomName) { public boolean isPublisherInRoom(String token, String session) {
if (SECURITY_ENABLED) { if (SECURITY_ENABLED) {
if (this.sessionIdTokenRole.get(roomName) != null){ if (this.sessionIdToken.get(session) != null){
if (this.sessionIdTokenRole.get(roomName).get(participantName) != null){ if (this.sessionIdToken.get(session).get(token) != null){
return (this.sessionIdTokenRole.get(roomName).get(participantName).equals(ParticipantRole.PUBLISHER)); return (this.sessionIdToken.get(session).get(token).getRole().equals(ParticipantRole.PUBLISHER));
} else { } else {
return false; return false;
} }
@ -968,29 +969,41 @@ public class RoomManager {
} }
} }
public void setTokenClientMetadata(String token, String session, String metadata){
if (this.sessionIdToken.get(session) != null){
if (this.sessionIdToken.get(session).get(token) != null){
this.sessionIdToken.get(session).get(token).setClientMetadata(metadata);
}
}
}
public String newSessionId(){ public String newSessionId(){
String sessionId = new BigInteger(130, new SecureRandom()).toString(32); String sessionId = new BigInteger(130, new SecureRandom()).toString(32);
this.sessionIdToken.put(sessionId, new ConcurrentHashMap<>());
this.sessionIdTokenRole.put(sessionId, new ConcurrentHashMap<>());
showMap(); showMap();
return sessionId; return sessionId;
} }
public String newToken(String sessionId, ParticipantRole role){ public String newToken(String session, ParticipantRole role, String metadata){
if (this.sessionIdTokenRole.get(sessionId) != null) { if (this.sessionIdToken.get(session) != null) {
String token = new BigInteger(130, new SecureRandom()).toString(32); if(metadataFormatCorrect(metadata)){
String token = new BigInteger(130, new SecureRandom()).toString(32);
this.sessionIdTokenRole.get(sessionId).put(token, role); this.sessionIdToken.get(session).put(token, new Token(token, role, metadata));
showMap();
showMap(); return token;
}
return token; else {
throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format. Max length allowed is 1000 chars");
}
} else { } else {
System.out.println("Error: the sessionId [" + sessionId + "] is not valid"); System.out.println("Error: the sessionId [" + session + "] is not valid");
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE,
"[" + sessionId +"] is not a valid sessionId"); "[" + session +"] is not a valid sessionId");
} }
} }
public boolean metadataFormatCorrect(String metadata){
// Max 1000 chars
return (metadata.length() <= 1000);
}
} }

View File

@ -78,6 +78,10 @@ public class DefaultNotificationRoomHandler implements NotificationRoomHandler {
JsonObject notifParams = new JsonObject(); JsonObject notifParams = new JsonObject();
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM, newUserName); notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM, newUserName);
// TO-DO: Return metadata associated to each participant
//notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM, participant.getUserName());
notifService.sendNotification(participant.getParticipantId(), notifService.sendNotification(participant.getParticipantId(),
ProtocolElements.PARTICIPANTJOINED_METHOD, notifParams); ProtocolElements.PARTICIPANTJOINED_METHOD, notifParams);
} }

View File

@ -91,25 +91,25 @@ public class Room {
return this.pipeline; return this.pipeline;
} }
public synchronized void join(String participantId, String userName, boolean dataChannels, public synchronized void join(String participantId, String user, boolean dataChannels,
boolean webParticipant) throws OpenViduException { boolean webParticipant) throws OpenViduException {
checkClosed(); checkClosed();
if (userName == null || userName.isEmpty()) { if (user == null || user.isEmpty()) {
throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Empty user name is not allowed"); throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Empty user name is not allowed");
} }
for (Participant p : participants.values()) { for (Participant p : participants.values()) {
if (p.getName().equals(userName)) { if (p.getName().equals(user)) {
throw new OpenViduException(Code.EXISTING_USER_IN_ROOM_ERROR_CODE, throw new OpenViduException(Code.EXISTING_USER_IN_ROOM_ERROR_CODE,
"User '" + userName + "' already exists in room '" + name + "'"); "User '" + user + "' already exists in room '" + name + "'");
} }
} }
createPipeline(); createPipeline();
Participant participant = Participant participant =
new Participant(participantId, userName, this, getPipeline(), dataChannels, webParticipant); new Participant(participantId, user, this, getPipeline(), dataChannels, webParticipant);
participants.put(participantId, participant); participants.put(participantId, participant);
filterStates.forEach((filterId, state) -> { filterStates.forEach((filterId, state) -> {
@ -117,7 +117,7 @@ public class Room {
roomHandler.updateFilter(name, participant, filterId, state); roomHandler.updateFilter(name, participant, filterId, state);
}); });
log.info("ROOM {}: Added participant {}", name, userName); log.info("ROOM {}: Added participant {}", name, user);
} }
public void newPublisher(Participant participant) { public void newPublisher(Participant participant) {

View File

@ -21,6 +21,7 @@ import static org.kurento.commons.PropertiesManager.getProperty;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.openvidu.client.OpenViduException;
import org.openvidu.server.core.NotificationRoomManager; import org.openvidu.server.core.NotificationRoomManager;
import org.openvidu.server.security.ParticipantRole; import org.openvidu.server.security.ParticipantRole;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -68,21 +69,30 @@ public class RoomController {
} }
@RequestMapping(value = "/newToken", method = RequestMethod.POST) @RequestMapping(value = "/newToken", method = RequestMethod.POST)
public ResponseEntity<JSONObject> getToken(@RequestBody Map sessionIdAndRole) { public ResponseEntity<JSONObject> getToken(@RequestBody Map sessionIdRoleMetadata) { // {0: sessionID, 1: role, 2: metadata}
JSONObject responseJson = new JSONObject(); String errorMessage = "";
try { try {
ParticipantRole role = ParticipantRole.valueOf((String) sessionIdAndRole.get("1")); ParticipantRole role = ParticipantRole.valueOf((String) sessionIdRoleMetadata.get("1"));
String token = roomManager.newToken((String) sessionIdAndRole.get("0"), role); String metadata = (String) sessionIdRoleMetadata.get("2");
String token = roomManager.newToken((String) sessionIdRoleMetadata.get("0"), role, metadata);
JSONObject responseJson = new JSONObject();
responseJson.put(0, token); responseJson.put(0, token);
return new ResponseEntity<JSONObject>(responseJson, HttpStatus.OK); return new ResponseEntity<JSONObject>(responseJson, HttpStatus.OK);
} }
catch (IllegalArgumentException e){ catch (IllegalArgumentException e){
responseJson.put("timestamp", System.currentTimeMillis()); return this.generateErrorResponse("Role " + sessionIdRoleMetadata.get("1") + " is not defined");
responseJson.put("status", HttpStatus.BAD_REQUEST.value()); } catch (OpenViduException e) {
responseJson.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase()); return this.generateErrorResponse("Metadata [" + sessionIdRoleMetadata.get("2") + "] unexpected format. Max length allowed is 1000 chars");
responseJson.put("message", "Role " + sessionIdAndRole.get("1") + " is not defined");
responseJson.put("path", "/newToken");
return new ResponseEntity<JSONObject>(responseJson, HttpStatus.BAD_REQUEST);
} }
} }
private ResponseEntity<JSONObject> generateErrorResponse(String errorMessage){
JSONObject responseJson = new JSONObject();
responseJson.put("timestamp", System.currentTimeMillis());
responseJson.put("status", HttpStatus.BAD_REQUEST.value());
responseJson.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
responseJson.put("message", errorMessage);
responseJson.put("path", "/newToken");
return new ResponseEntity<JSONObject>(responseJson, HttpStatus.BAD_REQUEST);
}
} }

View File

@ -51,26 +51,35 @@ public class JsonRpcUserControl {
public void joinRoom(Transaction transaction, Request<JsonObject> request, public void joinRoom(Transaction transaction, Request<JsonObject> request,
ParticipantRequest participantRequest) throws IOException, InterruptedException, ParticipantRequest participantRequest) throws IOException, InterruptedException,
ExecutionException { ExecutionException {
String roomName = getStringParam(request, ProtocolElements.JOINROOM_ROOM_PARAM);
String userName = getStringParam(request, ProtocolElements.JOINROOM_USER_PARAM); String session = getStringParam(request, ProtocolElements.JOINROOM_ROOM_PARAM);
String user = getStringParam(request, ProtocolElements.JOINROOM_USER_PARAM);
String userData = getStringParam(request, ProtocolElements.JOINROOM_METADATA_PARAM);
if(roomManager.getRoomManager().isParticipantInRoom(userName, roomName)){ if(roomManager.getRoomManager().isParticipantInRoom(user, session)){
boolean dataChannels = false; if(roomManager.getRoomManager().metadataFormatCorrect(userData)){
if (request.getParams().has(ProtocolElements.JOINROOM_DATACHANNELS_PARAM)) {
dataChannels = request.getParams().get(ProtocolElements.JOINROOM_DATACHANNELS_PARAM) this.roomManager.getRoomManager().setTokenClientMetadata(user, session, userData);
.getAsBoolean();
} boolean dataChannels = false;
if (request.getParams().has(ProtocolElements.JOINROOM_DATACHANNELS_PARAM)) {
ParticipantSession participantSession = getParticipantSession(transaction); dataChannels = request.getParams().get(ProtocolElements.JOINROOM_DATACHANNELS_PARAM)
participantSession.setParticipantName(userName); .getAsBoolean();
participantSession.setRoomName(roomName); }
participantSession.setDataChannels(dataChannels);
ParticipantSession participantSession = getParticipantSession(transaction);
roomManager.joinRoom(userName, roomName, dataChannels, true, participantRequest); participantSession.setParticipantName(user);
participantSession.setRoomName(session);
} participantSession.setDataChannels(dataChannels);
else {
roomManager.joinRoom(user, session, dataChannels, true, participantRequest);
} else {
System.out.println("Error: metadata format is incorrect");
throw new OpenViduException(Code.USER_METADATA_FORMAT_INVALID_ERROR_CODE,
"Unable to join room. The metadata received has an invalid format");
}
} else {
System.out.println("Error: sessionId or token not valid"); System.out.println("Error: sessionId or 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 does not have a valid token"); "Unable to join room. The user does not have a valid token");

View File

@ -0,0 +1,33 @@
package org.openvidu.server.security;
public class Token {
String token;
ParticipantRole role;
String serverMetadata;
String clientMetadata ;
public Token(String token, ParticipantRole role, String metadata) {
this.token = token;
this.role = role;
this.serverMetadata = metadata;
}
public String getToken() {
return token;
}
public ParticipantRole getRole() {
return role;
}
public void setClientMetadata(String metadata){
this.clientMetadata = metadata;
}
@Override
public String toString(){
return this.role.name();
}
}

View File

@ -6,4 +6,4 @@ server.ssl.keyStoreType: JKS
server.ssl.keyAlias: kurento-selfsigned server.ssl.keyAlias: kurento-selfsigned
openvidu.secret: MY_SECRET openvidu.secret: MY_SECRET
openvidu.security: false openvidu.security: true