Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/api/ApiResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class ApiResolver<Context> {
async execute(ctx: Context, method: string, params: unknown): Promise<unknown> {
const methodEntry = this.methods.get(method);
if (!methodEntry) {
throw new AppException("METHOD_NOT_FOUND");
throw new AppException("METHOD_NOT_FOUND", `Method '${method}' not found`);
}
const api = methodEntry.factory(ctx);
return await api.execute(methodEntry.method, params);
Expand Down
2 changes: 1 addition & 1 deletion src/api/AppException.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export class AppException extends Error {
if (ex instanceof AppException) {
throw ex;
}
throw new AppException("INTERNAL_ERROR");
throw new AppException("INTERNAL_ERROR", "An unexpected internal error occurred");
}

static is(e: any, errorName: ErrorCode): e is AppException {
Expand Down
2 changes: 1 addition & 1 deletion src/api/BaseApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class BaseApi {
async execute(method: string, params: any): Promise<unknown> {
const m = (this as any)[method];
if (!ApiMethod.getExportedMethod(this.constructor, method) || typeof(m) != "function") {
throw new AppException("METHOD_NOT_FOUND");
throw new AppException("METHOD_NOT_FOUND", `Method '${method}' not found`);
}
await this.validateAccess(method, params);
this.validateParams(method, params);
Expand Down
4 changes: 2 additions & 2 deletions src/api/JsonRpcServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ export class JsonRpcServer {
return JSON.parse(typeof(body) === "string" ? body : body.toString("utf8"));
}
catch {
throw new AppException("PARSE_ERROR");
throw new AppException("PARSE_ERROR", "Failed to parse JSON request body");
}
}

private async process(jRpc: any) {
if (!this.isJsonRpcRequest(jRpc)) {
throw new AppException("PARSE_ERROR");
throw new AppException("PARSE_ERROR", "Request is not a valid JSON-RPC object");
}
this.id = jRpc.id;
this.reportData(jRpc);
Expand Down
8 changes: 4 additions & 4 deletions src/api/ManagementBaseApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class ManagementBaseApi extends BaseApi {
async validateAccess() {
await this.authorizationDetector.authorizeByRequest();
if (!this.authorizationHolder.isAuthorized()) {
throw new AppException("UNAUTHORIZED");
throw new AppException("UNAUTHORIZED", "No valid API key or access token was provided");
}
}

Expand All @@ -42,7 +42,7 @@ export class ManagementBaseApi extends BaseApi {
const solutions: types.cloud.SolutionId[] = [];
const auth = this.authorizationHolder.getAuth();
if (!auth) {
throw new AppException("INSUFFICIENT_SCOPE");
throw new AppException("INSUFFICIENT_SCOPE", "Authorization token is missing or invalid");
}
const scopes = auth.session ? auth.session.scopes : auth.apiKey.scopes;
for (const scope of scopes) {
Expand All @@ -51,15 +51,15 @@ export class ManagementBaseApi extends BaseApi {
}
}
if (solutions.length === 0) {
throw new AppException("ACCESS_DENIED");
throw new AppException("ACCESS_DENIED", "No solutions found in authorization scope");
}
return solutions;
}

protected validateScope(scope: string) {
const auth = this.authorizationHolder.getAuth();
if (!auth) {
throw new AppException("UNAUTHORIZED");
throw new AppException("UNAUTHORIZED", "No valid API key or access token was provided");
}
const scopes = auth.session ? auth.session.scopes : auth.apiKey.scopes;
if (!scopes.includes(scope as types.auth.Scope)) {
Expand Down
24 changes: 12 additions & 12 deletions src/api/main/stream/StreamApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamPublish(model: streamApi.StreamPublishModel): Promise<streamApi.StreamPublishResult> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -115,7 +115,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamUpdate(model: streamApi.StreamUpdateModel): Promise<streamApi.StreamUpdateResult> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -126,7 +126,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamsSubscribeToRemote(model: streamApi.StreamsSubscribeModel): Promise<streamApi.StreamSubscribeResult> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -137,7 +137,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamsModifyRemoteSubscriptions(model: streamApi.StreamModifySubscriptionModel): Promise<streamApi.StreamSubscribeResult> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -148,7 +148,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamsUnsubscribeFromRemote(model: streamApi.StreamsUnsubscribeModel): Promise<streamApi.StreamSubscribeResult> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -159,7 +159,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamTrickle(model: streamApi.StreamTrickleModel): Promise<types.core.OK> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -170,7 +170,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamAcceptOffer(model: streamApi.StreamAcceptOfferModel): Promise<types.core.OK> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -181,7 +181,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamSetNewOffer(model: streamApi.StreamSetNewOfferModel): Promise<types.core.OK> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -198,7 +198,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamUnpublish(model: streamApi.StreamUnpublishModel): Promise<types.core.OK> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -217,7 +217,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamRoomJoin(model: streamApi.StreamRoomJoinModel): Promise<types.core.OK> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -228,7 +228,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamRoomLeave(model: streamApi.StreamRoomJoinModel): Promise<types.core.OK> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand All @@ -239,7 +239,7 @@ export class StreamApi extends BaseApi implements streamApi.IStreamApi {
@ApiMethod({})
async streamRoomEnableRecording(model: streamApi.StreamRoomRecordingModel): Promise<types.core.OK> {
if (!this.websocket) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires usage of Websocket only request");
}
const cloudUser = this.sessionService.validateContextSessionAndGetCloudUser();
const wsId = this.sessionService.getSessionUser().getWsId();
Expand Down
12 changes: 6 additions & 6 deletions src/api/main/user/UserApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class UserApi extends BaseApi implements userApi.IUserApi {
async authorizeWebSocket(model: {key: types.core.Base64, addWsChannelId: boolean}): Promise<{wsChannelId: types.core.WsChannelId}> {
this.sessionService.assertMethod(Permission.HAS_ANY_SESSION);
if (!this.webSocket) {
throw new AppException("WEBSOCKET_REQUIRED");
throw new AppException("WEBSOCKET_REQUIRED", "This method requires a WebSocket connection");
}
const wsChannelId = await this.webSocketConnectionManager.authorizeWebSocket(
this.getSession(), this.webSocket as WebSocketEx, !!model.addWsChannelId, model.key);
Expand All @@ -60,7 +60,7 @@ export class UserApi extends BaseApi implements userApi.IUserApi {
async unauthorizeWebSocket(): Promise<types.core.OK> {
this.sessionService.assertMethod(Permission.HAS_ANY_SESSION);
if (!this.webSocket) {
throw new AppException("WEBSOCKET_REQUIRED");
throw new AppException("WEBSOCKET_REQUIRED", "This method requires a WebSocket connection");
}
await this.webSocketConnectionManager.unauthorizeWebSocket(this.getSession(), this.webSocket as WebSocketEx);
return "OK";
Expand All @@ -70,7 +70,7 @@ export class UserApi extends BaseApi implements userApi.IUserApi {
async subscribeToChannel(model: {channel: types.core.WsChannelName}): Promise<userApi.SubscribeToChannelResult> {
this.sessionService.assertMethod(Permission.HAS_ANY_SESSION);
if (!this.webSocket) {
throw new AppException("WEBSOCKET_REQUIRED");
throw new AppException("WEBSOCKET_REQUIRED", "This method requires a WebSocket connection");
}
const subscriptionId = await this.webSocketConnectionManager.subscribeToChannelOld(this.getSession(), this.webSocket as WebSocketEx, model.channel);
return {subscriptionId};
Expand All @@ -80,7 +80,7 @@ export class UserApi extends BaseApi implements userApi.IUserApi {
async unsubscribeFromChannel(model: {channel: types.core.WsChannelName}): Promise<types.core.OK> {
this.sessionService.assertMethod(Permission.HAS_ANY_SESSION);
if (!this.webSocket) {
throw new AppException("WEBSOCKET_REQUIRED");
throw new AppException("WEBSOCKET_REQUIRED", "This method requires a WebSocket connection");
}
await this.webSocketConnectionManager.unsubscribeFromChannelOld(this.getSession(), this.webSocket as WebSocketEx, model.channel);
return "OK";
Expand All @@ -90,7 +90,7 @@ export class UserApi extends BaseApi implements userApi.IUserApi {
async subscribeToChannels(model: userApi.SubscribeToChannelsModel): Promise<userApi.SubscribeToChannelsResult> {
this.sessionService.assertMethod(Permission.HAS_ANY_SESSION);
if (!this.webSocket) {
throw new AppException("WEBSOCKET_REQUIRED");
throw new AppException("WEBSOCKET_REQUIRED", "This method requires a WebSocket connection");
}
const subscriptionsIds = await this.webSocketConnectionManager.subscribeToChannels(this.getSession(), this.webSocket as WebSocketEx, model.channels);
return {subscriptions: subscriptionsIds};
Expand All @@ -100,7 +100,7 @@ export class UserApi extends BaseApi implements userApi.IUserApi {
async unsubscribeFromChannels(model: userApi.UnsubscribeFromChannelsModel): Promise<types.core.OK> {
this.sessionService.assertMethod(Permission.HAS_ANY_SESSION);
if (!this.webSocket) {
throw new AppException("WEBSOCKET_REQUIRED");
throw new AppException("WEBSOCKET_REQUIRED", "This method requires a WebSocket connection");
}
await this.webSocketConnectionManager.unsubscribeFromChannels(this.getSession(), this.webSocket as WebSocketEx, model.subscriptionsIds);
return "OK";
Expand Down
10 changes: 5 additions & 5 deletions src/api/plain/context/ManagementContextApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class ManagementContextApi extends BaseApi implements managementContextAp
async validateAccess() {
await this.authorizationDetector.authorize();
if (!this.authorizationHolder.isAuthorized()) {
throw new AppException("UNAUTHORIZED");
throw new AppException("UNAUTHORIZED", "No valid API key or access token was provided");
}
}

Expand Down Expand Up @@ -179,7 +179,7 @@ export class ManagementContextApi extends BaseApi implements managementContextAp
return;
}
if (!solList.every(x => solutions.includes(x))) {
throw new AppException("ACCESS_DENIED");
throw new AppException("ACCESS_DENIED", "User does not have access to one or more of the requested solutions");
}
}

Expand All @@ -191,15 +191,15 @@ export class ManagementContextApi extends BaseApi implements managementContextAp
}
const context = await this.contextService.getContextWithCheckingExistance(contextId);
if (!solutions.includes(context.solution) && !context.shares.find(x => solutions.includes(x))) {
throw new AppException("ACCESS_DENIED");
throw new AppException("ACCESS_DENIED", "User does not have access to this context's solution");
}
}

private getSolutionsToWhichUserIsLimited() {
const solutions: types.cloud.SolutionId[] = [];
const auth = this.authorizationHolder.getAuth();
if (!auth) {
throw new AppException("INSUFFICIENT_SCOPE");
throw new AppException("INSUFFICIENT_SCOPE", "Authorization token is missing or invalid");
}
const scopes = auth.session ? auth.session.scopes : auth.apiKey.scopes;
for (const scope of scopes) {
Expand All @@ -213,7 +213,7 @@ export class ManagementContextApi extends BaseApi implements managementContextAp
private validateScope(scope: string) {
const auth = this.authorizationHolder.getAuth();
if (!auth) {
throw new AppException("UNAUTHORIZED");
throw new AppException("UNAUTHORIZED", "No valid API key or access token was provided");
}
const scopes = auth.session ? auth.session.scopes : auth.apiKey.scopes;
if (!scopes.includes(scope as types.auth.Scope)) {
Expand Down
10 changes: 5 additions & 5 deletions src/api/plain/manager/ManagerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class ManagerApi extends BaseApi implements managerApi.IManagerApi {
async validateAccess(method: string) {
await this.authorizationDetector.authorize();
if (method !== "auth" && method !== "bindAccessToken" && method !== "createFirstApiKey" && !this.authorizationHolder.isAuthorized()) {
throw new AppException("UNAUTHORIZED");
throw new AppException("UNAUTHORIZED", "No valid API key or access token was provided");
}
}

Expand Down Expand Up @@ -110,7 +110,7 @@ export class ManagerApi extends BaseApi implements managerApi.IManagerApi {
@ApiMethod({errorCodes: ["METHOD_CALLABLE_WITH_WEBSOCKET_ONLY"]})
async subscribeToChannel(model: managerApi.SubscribeToChannelModel): Promise<types.core.OK> {
if (!this.webSocketEx || !this.webSocketEx.ex.plainUserInfo) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires an active WebSocket connection with a plain user");
}
for (const channel of model.channels) {
this.validateScope(channel);
Expand All @@ -131,7 +131,7 @@ export class ManagerApi extends BaseApi implements managerApi.IManagerApi {
@ApiMethod({errorCodes: ["METHOD_CALLABLE_WITH_WEBSOCKET_ONLY"]})
async unsubscribeFromChannel(model: managerApi.UnsubscribeFromChannelModel): Promise<types.core.OK> {
if (!this.webSocketEx || !this.webSocketEx.ex.plainUserInfo) {
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY");
throw new AppException("METHOD_CALLABLE_WITH_WEBSOCKET_ONLY", "This method requires an active WebSocket connection with a plain user");
}
for (const channel of model.channels) {
this.validateScope(channel);
Expand All @@ -153,7 +153,7 @@ export class ManagerApi extends BaseApi implements managerApi.IManagerApi {
private validateScope(scope: string) {
const auth = this.authorizationHolder.getAuth();
if (!auth) {
throw new AppException("UNAUTHORIZED");
throw new AppException("UNAUTHORIZED", "Authorization required");
}
const scopes = auth.session ? auth.session.scopes : auth.apiKey.scopes;
if (!scopes.includes(scope as types.auth.Scope)) {
Expand All @@ -165,7 +165,7 @@ export class ManagerApi extends BaseApi implements managerApi.IManagerApi {
const solutions: types.cloud.SolutionId[] = [];
const auth = this.authorizationHolder.getAuth();
if (!auth) {
throw new AppException("INSUFFICIENT_SCOPE");
throw new AppException("INSUFFICIENT_SCOPE", "Authorization token is missing or invalid");
}
const scopes = auth.session ? auth.session.scopes : auth.apiKey.scopes;
for (const scope of scopes) {
Expand Down
Loading