Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into ref/…
Browse files Browse the repository at this point in the history
…admin_permissions

* 'develop' of github.com:RocketChat/Rocket.Chat:
  [FIX] "Save to WebDav" not working (#18883)
  [FIX] If there is `ufs` somewhere in url the request to api always returns 404 (#18874)
  LingoHub based on develop (#18828)
  [FIX] Showing alerts during setup wizard (#18862)
  [FIX] Jitsi call start updating subscriptions (#18837)
  [FIX] Omnichannel Current Chats open status filter not working (#18795)
  [FIX] User can't invite or join other Omnichannel rooms (#18852)
  [FIX][ENTERPRISE] Omnichannel service status switching to unavailable (#18835)
  [FIX] Admin user blank page (#18851)
  • Loading branch information
gabriellsh committed Sep 16, 2020
2 parents 1b8072d + 6df12f8 commit c55ce6c
Show file tree
Hide file tree
Showing 37 changed files with 3,838 additions and 56 deletions.
28 changes: 22 additions & 6 deletions app/2fa/server/code/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function getUserForCheck(userId: string): IUser {
fields: {
emails: 1,
language: 1,
createdAt: 1,
'services.totp': 1,
'services.email2fa': 1,
'services.emailCode': 1,
Expand All @@ -61,6 +62,19 @@ export function getFingerprintFromConnection(connection: IMethodConnection): str
return crypto.createHash('md5').update(data).digest('hex');
}

function getRememberDate(from: Date = new Date()): Date | undefined {
const rememberFor = parseInt(settings.get('Accounts_TwoFactorAuthentication_RememberFor') as string, 10);

if (rememberFor <= 0) {
return;
}

const expires = new Date(from);
expires.setSeconds(expires.getSeconds() + rememberFor);

return expires;
}

export function isAuthorizedForToken(connection: IMethodConnection, user: IUser, options: ITwoFactorOptions): boolean {
const currentToken = Accounts._getLoginToken(connection.id);
const tokenObject = user.services?.resume?.loginTokens?.find((i) => i.hashedToken === currentToken);
Expand All @@ -77,6 +91,12 @@ export function isAuthorizedForToken(connection: IMethodConnection, user: IUser,
return false;
}

// remember user right after their registration
const rememberAfterRegistration = user.createdAt && getRememberDate(user.createdAt);
if (rememberAfterRegistration && rememberAfterRegistration >= new Date()) {
return true;
}

if (!tokenObject.twoFactorAuthorizedUntil || !tokenObject.twoFactorAuthorizedHash) {
return false;
}
Expand All @@ -95,15 +115,11 @@ export function isAuthorizedForToken(connection: IMethodConnection, user: IUser,
export function rememberAuthorization(connection: IMethodConnection, user: IUser): void {
const currentToken = Accounts._getLoginToken(connection.id);

const rememberFor = parseInt(settings.get('Accounts_TwoFactorAuthentication_RememberFor') as string, 10);

if (rememberFor <= 0) {
const expires = getRememberDate();
if (!expires) {
return;
}

const expires = new Date();
expires.setSeconds(expires.getSeconds() + rememberFor);

Users.setTwoFactorAuthorizationHashAndUntilForUserIdAndToken(user._id, currentToken, getFingerprintFromConnection(connection), expires);
}

Expand Down
2 changes: 1 addition & 1 deletion app/lib/server/functions/addUserToRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const addUserToRoom = function(rid, user, inviter, silenced) {
throw error;
}

if (room.t === 'c' || room.t === 'p') {
if (room.t === 'c' || room.t === 'p' || room.t === 'l') {
// Add a new event, with an optional inviter
callbacks.run('beforeAddedToRoom', { user, inviter }, room);

Expand Down
2 changes: 1 addition & 1 deletion app/lib/server/startup/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2679,7 +2679,7 @@ settings.addGroup('Setup_Wizard', function() {
this.add('Allow_Marketing_Emails', true, {
type: 'boolean',
});
this.add('Register_Server', true, {
this.add('Register_Server', false, {
type: 'boolean',
});
this.add('Organization_Email', '', {
Expand Down
6 changes: 5 additions & 1 deletion app/livechat/lib/LivechatRoomType.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ChatRoom } from '../../models';
import { settings } from '../../settings';
import { hasPermission } from '../../authorization';
import { openRoom } from '../../ui-utils';
import { RoomSettingsEnum, UiTextContext, RoomTypeRouteConfig, RoomTypeConfig } from '../../utils';
import { RoomMemberActions, RoomSettingsEnum, UiTextContext, RoomTypeRouteConfig, RoomTypeConfig } from '../../utils';
import { getAvatarURL } from '../../utils/lib/getAvatarURL';

let LivechatInquiry;
Expand Down Expand Up @@ -85,6 +85,10 @@ export default class LivechatRoomType extends RoomTypeConfig {
}
}

allowMemberAction(room, action) {
return [RoomMemberActions.INVITE, RoomMemberActions.JOIN].includes(action);
}

getUiText(context) {
switch (context) {
case UiTextContext.HIDE_WARNING:
Expand Down
8 changes: 8 additions & 0 deletions app/livechat/server/startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ Meteor.startup(async () => {
}));
}, callbacks.priority.LOW, 'cant-leave-room');

callbacks.add('beforeJoinRoom', function(user, room) {
if (room.t === 'l' && !hasPermission(user._id, 'view-l-room')) {
throw new Meteor.Error('error-user-is-not-agent', 'User is not an Omnichannel Agent', { method: 'beforeJoinRoom' });
}

return user;
}, callbacks.priority.LOW, 'cant-join-room');

createLivechatQueueView();

const monitor = new LivechatAgentActivityMonitor();
Expand Down
2 changes: 1 addition & 1 deletion app/tokenpass/server/startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Meteor.startup(function() {
throw new Meteor.Error('error-not-allowed', 'Token required', { method: 'joinRoom' });
}

return room;
return user;
});
});

Expand Down
6 changes: 0 additions & 6 deletions app/videobridge/server/methods/jitsiSetTimeout.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,6 @@ Meteor.methods({
],
});
message.msg = TAPi18n.__('Started_a_video_call');
message.mentions = [
{
_id: 'here',
username: 'here',
},
];
callbacks.run('afterSaveMessage', message, { ...room, jitsiTimeout: currentTime + CONSTANTS.TIMEOUT });
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { createClient } from 'webdav';

import type { WebDavClient, Stat } from '../../../../definition/webdav';

export type ServerCredentials = {
token?: string;
username?: string;
password?: string;
};

export class WebdavClientAdapter {
constructor(serverConfig, cred) {
_client: WebDavClient;

constructor(serverConfig: string, cred: ServerCredentials) {
if (cred.token) {
this._client = createClient(
serverConfig,
Expand All @@ -18,51 +28,59 @@ export class WebdavClientAdapter {
}
}

async stat(path) {
async stat(path: string): Promise<undefined> {
try {
return await this._client.stat(path);
} catch (error) {
throw new Error(error.response && error.response.statusText ? error.response.statusText : 'Error checking if directory exists on webdav');
}
}

async createDirectory(path) {
async createDirectory(path: string): Promise<Response> {
try {
return await this._client.createDirectory(path);
} catch (error) {
throw new Error(error.response && error.response.statusText ? error.response.statusText : 'Error creating directory on webdav');
}
}

async deleteFile(path) {
async deleteFile(path: string): Promise<Response> {
try {
return await this._client.deleteFile(path);
} catch (error) {
throw new Error(error.response && error.response.statusText ? error.response.statusText : 'Error deleting file on webdav');
}
}

async getFileContents(filename) {
async getFileContents(filename: string): Promise<Buffer> {
try {
return await this._client.getFileContents(filename);
return await this._client.getFileContents(filename) as Buffer;
} catch (error) {
throw new Error(error.response && error.response.statusText ? error.response.statusText : 'Error getting file contents webdav');
}
}

async getDirectoryContents(path) {
async getDirectoryContents(path: string): Promise<Array<Stat>> {
try {
return await this._client.getDirectoryContents(path);
} catch (error) {
throw new Error(error.response && error.response.statusText ? error.response.statusText : 'Error getting directory contents webdav');
}
}

createReadStream(path, options) {
async putFileContents(path: string, data: Buffer, options: Record<string, any> = {}): Promise<any> {
try {
return await this._client.putFileContents(path, data, options);
} catch (error) {
throw new Error(error.response?.statusText ?? 'Error updating file contents.');
}
}

createReadStream(path: string, options?: Record<string, any>): ReadableStream {
return this._client.createReadStream(path, options);
}

createWriteStream(path) {
createWriteStream(path: string): WritableStream {
return this._client.createWriteStream(path);
}
}
7 changes: 0 additions & 7 deletions app/webdav/server/methods/getWebdavCredentials.js

This file was deleted.

9 changes: 9 additions & 0 deletions app/webdav/server/methods/getWebdavCredentials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ServerCredentials } from '../lib/webdavClientAdapter';

export function getWebdavCredentials(account: ServerCredentials): ServerCredentials {
const cred = account.token ? { token: account.token } : {
username: account.username,
password: account.password,
};
return cred;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor';

import { settings } from '../../../settings';
import { Logger } from '../../../logger';
import { settings } from '../../../settings/server';
import { Logger } from '../../../logger/server';
import { getWebdavCredentials } from './getWebdavCredentials';
import { WebdavAccounts } from '../../../models';
import { WebdavAccounts } from '../../../models/server';
import { WebdavClientAdapter } from '../lib/webdavClientAdapter';

const logger = new Logger('WebDAV_Upload', {});
Expand All @@ -29,11 +29,14 @@ Meteor.methods({
try {
const cred = getWebdavCredentials(account);
const client = new WebdavClientAdapter(account.server_url, cred);
// eslint-disable-next-line @typescript-eslint/no-empty-function
await client.createDirectory(uploadFolder).catch(() => {});
await client.putFileContents(`${ uploadFolder }/${ name }`, buffer, { overwrite: false });
return { success: true };
} catch (error) {
// @ts-ignore
logger.error(error);

if (error.response) {
const { status } = error.response;
if (status === 404) {
Expand Down
4 changes: 3 additions & 1 deletion client/admin/users/UserInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ export function UserInfoWithData({ uid, username, ...props }) {
return <Box mbs='x16'>{t('User_not_found')}</Box>;
}

const admin = data.user?.roles?.includes('admin');

return <UserInfo
{...user}
data={data.user}
onChange={onChange}
actions={data && data.user && <UserInfoActions isActive={data.user.active} isAdmin={data.user.roles.includes('admin')} _id={data.user._id} username={data.user.username} onChange={onChange}/>}
actions={data && data.user && <UserInfoActions isActive={data.user.active} isAdmin={admin} _id={data.user._id} username={data.user.username} onChange={onChange}/>}
{...props}
/>;
}
2 changes: 1 addition & 1 deletion client/omnichannel/currentChats/CurrentChatsRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const useQuery = ({ guest, servedBy, department, status, from, to, tags, customF
query.createdAt = JSON.stringify({ start: from, end: to });
}
if (status !== 'all') {
query.open = status === 'open';
query.open = status === 'opened';
}
if (servedBy && servedBy !== 'all') {
query.agents = [servedBy];
Expand Down
32 changes: 32 additions & 0 deletions definition/webdav.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export type Stat = {
filename: string;
basename: string;
lastmod: string|null;
size: number;
type: string;
mime: string;
etag: string|null;
props: Record<string, any>;
}

export type WebDavClient = {
copyFile(remotePath: string, targetRemotePath: string, options?: Record<string, any>): Promise<any>;
createDirectory(dirPath: string, options?: Record<string, any>): Promise<Response>;
createReadStream(remoteFileName: string, options?: Record<string, any>): ReadableStream;
createWriteStream(remoteFileName: string, options?: Record<string, any>, callback?: Function): WritableStream;
customRequest(remotePath: string, requestOptions: Record<string, any>, options?: Record<string, any>): Promise<any>;
deleteFile(remotePath: string, options?: Record<string, any>): Promise<Response>;
exists(remotePath: string, options?: Record<string, any>): Promise<boolean>;
getDirectoryContents(remotePath: string, options?: Record<string, any>): Promise<Array<Stat>>;
getFileContents(remoteFileName: string, options?: Record<string, any>): Promise<Buffer|string>;
getFileDownloadLink(remoteFileName: string, options?: Record<string, any>): string;
getFileUploadLink(remoteFileName: string, options?: Record<string, any>): string;
getQuota(options?: Record<string, any>): Promise<null|object>;
moveFile(remotePath: string, targetRemotePath: string, options?: Record<string, any>): Promise<any>;
putFileContents(remoteFileName: string, data: string|Buffer, options?: Record<string, any>): Promise<any>;
stat(remotePath: string, options?: Record<string, any>): Promise<any>;
}

declare module 'webdav' {
export function createClient(remoteURL: string, opts?: Record<string, any>): WebDavClient;
}
4 changes: 3 additions & 1 deletion ee/app/livechat-enterprise/server/startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ Meteor.startup(async function() {
});
settings.onload('Livechat_business_hour_type', (_, value) => {
businessHourManager.registerBusinessHourBehavior(businessHours[value]);
businessHourManager.startManager();
if (settings.get('Livechat_enable_business_hours')) {
businessHourManager.startManager();
}
});
await resetDefaultBusinessHourIfNeeded();
});
2 changes: 2 additions & 0 deletions packages/meteor-jalik-ufs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.npm
.idea
21 changes: 21 additions & 0 deletions packages/meteor-jalik-ufs/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2017 Karl STEIN

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading

0 comments on commit c55ce6c

Please sign in to comment.