mirror of https://github.com/OpenVidu/openvidu.git
openvidu-pro: Adapt openvidu-browser and openvidu-server ce to send browser logs related with openvidu-browser in OpenVidu Pro
parent
da3fb64073
commit
5841d15a86
|
@ -1322,6 +1322,10 @@ export class Session extends EventDispatcher {
|
|||
reject(error);
|
||||
} else {
|
||||
|
||||
// Configure JSNLogs
|
||||
OpenViduLogger.configureJSNLog(this.openvidu, this.sessionId, response.id, token);
|
||||
|
||||
// Process join room response
|
||||
this.processJoinRoomResponse(response);
|
||||
|
||||
// Initialize local Connection object with values returned by openvidu-server
|
||||
|
@ -1461,7 +1465,6 @@ export class Session extends EventDispatcher {
|
|||
|
||||
this.openvidu.wsUri = 'wss://' + url.host + '/openvidu';
|
||||
this.openvidu.httpUri = 'https://' + url.host;
|
||||
|
||||
} else {
|
||||
logger.error('Token "' + token + '" is not valid')
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var Logger = console;
|
||||
var OpenViduLogger = require('../../../../Logger/OpenViduLogger').OpenViduLogger;
|
||||
var Logger = OpenViduLogger.getInstance();
|
||||
|
||||
var MAX_RETRIES = 2000; // Forever...
|
||||
var RETRY_TIME_MS = 3000; // FIXME: Implement exponential wait times...
|
||||
|
|
|
@ -1,12 +1,87 @@
|
|||
import {JL} from 'jsnlog'
|
||||
import {OpenVidu} from "../../OpenVidu/OpenVidu";
|
||||
|
||||
export class OpenViduLogger {
|
||||
|
||||
private static instance: OpenViduLogger;
|
||||
|
||||
private JSNLOG_URL = "/openvidu/elk/openvidu-browser-logs";
|
||||
private MAX_JSNLOG_BATCH_LOG_MESSAGES: number = 50;
|
||||
private MAX_MSECONDS_BATCH_MESSAGES: number = 5000;
|
||||
|
||||
private openvidu: OpenVidu;
|
||||
private logger: Console = window.console;
|
||||
private LOG_FNS = [this.logger.log, this.logger.debug, this.logger.info, this.logger.warn, this.logger.error];
|
||||
private isProdMode = false;
|
||||
private isJSNLogEnabled = true;
|
||||
private isJSNLogSetup = false;
|
||||
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* Configure http uri to send logs using JSNlog
|
||||
*/
|
||||
static configureJSNLog(openVidu: OpenVidu, sessionId: string, connectionId: string, token: string) {
|
||||
// If instance is not null, JSNLog is enabled and is OpenVidu Pro
|
||||
if (this.instance && this.instance.isJSNLogEnabled && openVidu.webrtcStatsInterval > -1) {
|
||||
this.instance.info("Configuring JSNLogs.");
|
||||
try {
|
||||
this.instance.openvidu = openVidu;
|
||||
|
||||
// Use connection id as user and token as password
|
||||
const openViduJSNLogHeaders = (xhr) => {
|
||||
xhr.setRequestHeader('Authorization', "Basic " + btoa(connectionId + ":" + token));
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
|
||||
// Additional headers for OpenVidu
|
||||
xhr.setRequestHeader('OV-Connection-Id', connectionId);
|
||||
xhr.setRequestHeader('OV-Session-Id', sessionId);
|
||||
}
|
||||
|
||||
const customAppender: any = JL.createAjaxAppender("openvidu-browser-logs-appender-" + connectionId);
|
||||
customAppender.setOptions({
|
||||
beforeSend: openViduJSNLogHeaders,
|
||||
batchSize: this.instance.MAX_JSNLOG_BATCH_LOG_MESSAGES,
|
||||
maxBatchSize: this.instance.MAX_JSNLOG_BATCH_LOG_MESSAGES,
|
||||
batchTimeout: this.instance.MAX_MSECONDS_BATCH_MESSAGES
|
||||
});
|
||||
|
||||
// Avoid circular dependencies
|
||||
const logSerializer = (obj): string => {
|
||||
const getCircularReplacer = () => {
|
||||
const seen = new WeakSet();
|
||||
return (key, value) => {
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (seen.has(value)) {
|
||||
return;
|
||||
}
|
||||
seen.add(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
};
|
||||
return JSON.stringify(obj, getCircularReplacer());
|
||||
};
|
||||
|
||||
// Initialize JL to send logs
|
||||
JL.setOptions({
|
||||
defaultAjaxUrl: OpenViduLogger.instance.openvidu.httpUri + this.instance.JSNLOG_URL,
|
||||
serialize: logSerializer
|
||||
});
|
||||
JL().setOptions({
|
||||
appenders: [customAppender]
|
||||
});
|
||||
|
||||
this.instance.isJSNLogSetup = true;
|
||||
this.instance.info("JSNLog configured.");
|
||||
} catch (e) {
|
||||
console.error("Error configuring JSNLog: ");
|
||||
console.error(e);
|
||||
this.instance.isJSNLogSetup = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getInstance(): OpenViduLogger {
|
||||
if(!OpenViduLogger.instance){
|
||||
OpenViduLogger.instance = new OpenViduLogger();
|
||||
|
@ -18,31 +93,55 @@ export class OpenViduLogger {
|
|||
if (!this.isProdMode) {
|
||||
this.LOG_FNS[0].apply(this.logger, arguments);
|
||||
}
|
||||
if (this.isMonitoringLogEnabled()) {
|
||||
JL().info(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
debug(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.LOG_FNS[1].apply(this.logger, arguments);
|
||||
}
|
||||
if (this.isMonitoringLogEnabled()) {
|
||||
JL().debug(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
info(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.LOG_FNS[2].apply(this.logger, arguments);
|
||||
}
|
||||
if (this.isMonitoringLogEnabled()) {
|
||||
JL().info(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
warn(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.LOG_FNS[3].apply(this.logger, arguments);
|
||||
}
|
||||
if (this.isMonitoringLogEnabled()) {
|
||||
JL().warn(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
error(...args: any[]) {
|
||||
this.LOG_FNS[4].apply(this.logger, arguments);
|
||||
if (this.isMonitoringLogEnabled()) {
|
||||
JL().error(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
enableProdMode(){
|
||||
this.isProdMode = true;
|
||||
}
|
||||
|
||||
disableBrowserLogsMonitoring() {
|
||||
this.isJSNLogEnabled = false;
|
||||
}
|
||||
|
||||
private isMonitoringLogEnabled() {
|
||||
return this.isJSNLogEnabled && this.isJSNLogSetup;
|
||||
}
|
||||
|
||||
}
|
|
@ -24,6 +24,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import io.openvidu.server.core.TokenRegister;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.kurento.jsonrpc.internal.server.config.JsonRpcConfiguration;
|
||||
import org.kurento.jsonrpc.server.JsonRpcConfigurer;
|
||||
|
@ -169,6 +170,13 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
|||
return new TokenGenerator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@DependsOn("openviduConfig")
|
||||
public TokenRegister tokenRegister() {
|
||||
return new TokenRegister();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@DependsOn("openviduConfig")
|
||||
|
|
|
@ -56,16 +56,16 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
.csrf().disable().authorizeRequests()
|
||||
.antMatchers(HttpMethod.GET, RequestMappings.API + "/config/openvidu-publicurl").permitAll()
|
||||
.antMatchers(HttpMethod.GET, RequestMappings.ACCEPT_CERTIFICATE).permitAll()
|
||||
.antMatchers(RequestMappings.API + "/**").authenticated()
|
||||
.antMatchers(HttpMethod.GET, RequestMappings.CDR + "/**").authenticated()
|
||||
.antMatchers(HttpMethod.GET, RequestMappings.FRONTEND_CE + "/**").authenticated()
|
||||
.antMatchers(HttpMethod.GET, RequestMappings.CUSTOM_LAYOUTS + "/**").authenticated();
|
||||
.antMatchers(RequestMappings.API + "/**").hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.GET, RequestMappings.CDR + "/**").hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.GET, RequestMappings.FRONTEND_CE + "/**").hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.GET, RequestMappings.CUSTOM_LAYOUTS + "/**").hasRole("ADMIN");
|
||||
|
||||
// Secure recordings depending on OPENVIDU_RECORDING_PUBLIC_ACCESS
|
||||
if (openviduConf.getOpenViduRecordingPublicAccess()) {
|
||||
conf = conf.antMatchers(HttpMethod.GET, RequestMappings.RECORDINGS + "/**").permitAll();
|
||||
} else {
|
||||
conf = conf.antMatchers(HttpMethod.GET, RequestMappings.RECORDINGS + "/**").authenticated();
|
||||
conf = conf.antMatchers(HttpMethod.GET, RequestMappings.RECORDINGS + "/**").hasRole("ADMIN");
|
||||
}
|
||||
|
||||
conf.and().httpBasic();
|
||||
|
|
|
@ -81,12 +81,16 @@ public abstract class SessionManager {
|
|||
@Autowired
|
||||
protected TokenGenerator tokenGenerator;
|
||||
|
||||
@Autowired
|
||||
protected TokenRegister tokenRegister;
|
||||
|
||||
@Autowired
|
||||
protected QuarantineKiller quarantineKiller;
|
||||
|
||||
@Autowired
|
||||
protected GeoLocationByIp geoLocationByIp;
|
||||
|
||||
|
||||
public FormatChecker formatChecker = new FormatChecker();
|
||||
|
||||
private UpdatableTimerTask sessionGarbageCollectorTimer;
|
||||
|
@ -327,6 +331,7 @@ public abstract class SessionManager {
|
|||
Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), serverMetadata, record, role,
|
||||
kurentoOptions);
|
||||
session.storeToken(tokenObj);
|
||||
tokenRegister.registerToken(session.getSessionId(), tokenObj);
|
||||
return tokenObj;
|
||||
}
|
||||
|
||||
|
@ -335,6 +340,7 @@ public abstract class SessionManager {
|
|||
Token tokenObj = new Token(token, session.getSessionId(), connectionProperties,
|
||||
this.openviduConfig.isTurnadminAvailable() ? this.coturnCredentialsService.createUser() : null);
|
||||
session.storeToken(tokenObj);
|
||||
tokenRegister.registerToken(session.getSessionId(), tokenObj);
|
||||
return tokenObj;
|
||||
}
|
||||
|
||||
|
@ -623,6 +629,7 @@ public abstract class SessionManager {
|
|||
sessionidParticipantpublicidParticipant.remove(sessionId);
|
||||
sessionidFinalUsers.remove(sessionId);
|
||||
sessionidAccumulatedRecordings.remove(sessionId);
|
||||
tokenRegister.deregisterTokens(sessionId);
|
||||
}
|
||||
|
||||
private void initializeCollections(String sessionId) {
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package io.openvidu.server.core;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* This service class represents all the tokens currently registered by an active sessions
|
||||
* Each time a token is created, it is registered by {@link SessionManager} using {@link #registerToken(String, Token)}
|
||||
* All registered tokens will be present until {@link SessionManager} calls the method {@link #deregisterTokens(String)}
|
||||
*
|
||||
* The purpose of this service is to know when a token was registered into a session by using
|
||||
* public method {@link #isTokenRegistered(String)}
|
||||
*/
|
||||
public class TokenRegister {
|
||||
|
||||
private ConcurrentHashMap<String, Token> tokensRegistered = new ConcurrentHashMap<>();
|
||||
private ConcurrentHashMap<String, ConcurrentHashMap<String, Token>> tokensRegisteredBySession = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Register a token of an specific active session
|
||||
* @param sessionId Id of the sessions where the token is generated
|
||||
* @param token Token to register
|
||||
*/
|
||||
protected synchronized void registerToken(String sessionId, Token token) {
|
||||
this.tokensRegisteredBySession.putIfAbsent(sessionId, new ConcurrentHashMap<>());
|
||||
ConcurrentHashMap<String, Token> registeredTokensInSession = this.tokensRegisteredBySession.get(sessionId);
|
||||
this.tokensRegistered.put(token.getToken(), token);
|
||||
registeredTokensInSession.put(token.getToken(), token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregister all tokens of an specific session which is not active
|
||||
* @param sessionId Id of the session which is no longer active
|
||||
*/
|
||||
protected synchronized void deregisterTokens(String sessionId) {
|
||||
if (tokensRegisteredBySession.containsKey(sessionId)) {
|
||||
for(Map.Entry<String, Token> tokenRegisteredInSession: tokensRegistered.entrySet()) {
|
||||
tokensRegistered.remove(tokenRegisteredInSession.getKey());
|
||||
}
|
||||
tokensRegisteredBySession.remove(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current token string was registered in an active session
|
||||
* @param token Token string to check if it is registered
|
||||
* @return <code>true</code> if token was registered. <code>false</code> otherwise
|
||||
*/
|
||||
public boolean isTokenRegistered(String token) {
|
||||
return this.tokensRegistered.containsKey(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered token from token string
|
||||
* @param tokenKey string key which represents the token
|
||||
* @return
|
||||
*/
|
||||
public Token getRegisteredToken(String tokenKey) {
|
||||
Token token = this.tokensRegistered.get(tokenKey);
|
||||
if (token != null) {
|
||||
return token;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -120,25 +120,25 @@ public class ApiRestPathRewriteFilter implements Filter {
|
|||
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry conf,
|
||||
OpenviduConfig openviduConf) throws Exception {
|
||||
|
||||
conf.antMatchers("/api/**").authenticated()
|
||||
conf.antMatchers("/api/**").hasRole("ADMIN")
|
||||
// /config
|
||||
.antMatchers(HttpMethod.GET, "/config/openvidu-publicurl").permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/config/**").authenticated()
|
||||
.antMatchers(HttpMethod.GET, "/config/**").hasRole("ADMIN")
|
||||
// /cdr
|
||||
.antMatchers(HttpMethod.GET, "/cdr/**").authenticated()
|
||||
.antMatchers(HttpMethod.GET, "/cdr/**").hasRole("ADMIN")
|
||||
// /accept-certificate
|
||||
.antMatchers(HttpMethod.GET, "/accept-certificate").permitAll()
|
||||
// Dashboard
|
||||
.antMatchers(HttpMethod.GET, "/dashboard/**").authenticated();
|
||||
.antMatchers(HttpMethod.GET, "/dashboard/**").hasRole("ADMIN");
|
||||
|
||||
// Security for recording layouts
|
||||
conf.antMatchers("/layouts/**").authenticated();
|
||||
conf.antMatchers("/layouts/**").hasRole("ADMIN");
|
||||
|
||||
// Security for recorded video files
|
||||
if (openviduConf.getOpenViduRecordingPublicAccess()) {
|
||||
conf = conf.antMatchers("/recordings/**").permitAll();
|
||||
} else {
|
||||
conf = conf.antMatchers("/recordings/**").authenticated();
|
||||
conf = conf.antMatchers("/recordings/**").hasRole("ADMIN");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ server.ssl.key-store=classpath:openvidu-selfsigned.jks
|
|||
server.ssl.key-store-password=openvidu
|
||||
server.ssl.key-store-type=JKS
|
||||
server.ssl.key-alias=openvidu-selfsigned
|
||||
server.servlet.session.cookie.name=OVJSESSIONID
|
||||
|
||||
logging.level.root=info
|
||||
spring.main.allow-bean-definition-overriding=true
|
||||
|
|
Loading…
Reference in New Issue