Files
mattermostest/src/utils/util.js
Guillermo Vayá 36c6106cad [MM-22648] basic auth external sites (#1295)
* fix not using babel

* wip

* added tests, moved to map, polifill-like to convert between object and map

* basic structure setup

* working, found new bug

* change buttons

* fix login issue

* remove logging code

* address CR comments

* remove custom function in favor of airbnb shim

* fix linting

* fix PM requested changes

* [MM-25323] fix basic auth cancelling

* fix crash when multiple request were made

* address UX comments, added external link for user
convenience
2020-05-28 10:53:57 +02:00

196 lines
5.4 KiB
JavaScript

// Copyright (c) 2015-2016 Yuya Ochiai
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import url from 'url';
import electron, {remote} from 'electron';
import log from 'electron-log';
import {isUri, isHttpUri, isHttpsUri} from 'valid-url';
function getDomain(inputURL) {
const parsedURL = url.parse(inputURL);
return `${parsedURL.protocol}//${parsedURL.host}`;
}
function isValidURL(testURL) {
return Boolean(isHttpUri(testURL) || isHttpsUri(testURL));
}
function isValidURI(testURL) {
return Boolean(isUri(testURL));
}
function parseURL(inputURL) {
if (!inputURL) {
return null;
}
if (inputURL instanceof URL) {
return inputURL;
}
try {
return new URL(inputURL);
} catch (e) {
return null;
}
}
function getHost(inputURL) {
const parsedURL = parseURL(inputURL);
if (parsedURL) {
return parsedURL.origin;
}
throw new Error(`Couldn't parse url: ${inputURL}`);
}
// isInternalURL determines if the target url is internal to the application.
// - currentURL is the current url inside the webview
// - basename is the global export from the Mattermost application defining the subpath, if any
function isInternalURL(targetURL, currentURL, basename = '/') {
if (targetURL.host !== currentURL.host) {
return false;
}
if (!(targetURL.pathname || '/').startsWith(basename)) {
return false;
}
return true;
}
function getServerInfo(serverUrl) {
const parsedServer = parseURL(serverUrl);
if (!parsedServer) {
return null;
}
// does the server have a subpath?
const pn = parsedServer.pathname.toLowerCase();
const subpath = pn.endsWith('/') ? pn.toLowerCase() : `${pn}/`;
return {origin: parsedServer.origin, subpath, url: parsedServer};
}
function isTeamUrl(serverUrl, inputUrl, withApi) {
const parsedURL = parseURL(inputUrl);
const server = getServerInfo(serverUrl);
if (!parsedURL || !server || (!equalUrlsIgnoringSubpath(server, parsedURL))) {
return null;
}
const nonTeamUrlPaths = ['plugins', 'signup', 'login', 'admin', 'channel', 'post', 'oauth', 'admin_console'];
if (withApi) {
nonTeamUrlPaths.push('api');
}
return !(nonTeamUrlPaths.some((testPath) => (
parsedURL.pathname.toLowerCase().startsWith(`${server.subpath}${testPath}/`) ||
parsedURL.pathname.toLowerCase().startsWith(`/${testPath}/`))));
}
function isPluginUrl(serverUrl, inputURL) {
const server = getServerInfo(serverUrl);
const parsedURL = parseURL(inputURL);
if (!parsedURL || !server) {
return false;
}
return (
equalUrlsIgnoringSubpath(server, parsedURL) &&
(parsedURL.pathname.toLowerCase().startsWith(`${server.subpath}plugins/`) ||
parsedURL.pathname.toLowerCase().startsWith('/plugins/')));
}
function getServer(inputURL, teams) {
const parsedURL = parseURL(inputURL);
if (!parsedURL) {
return null;
}
let parsedServerUrl;
let secondOption = null;
for (let i = 0; i < teams.length; i++) {
parsedServerUrl = parseURL(teams[i].url);
// check server and subpath matches (without subpath pathname is \ so it always matches)
if (equalUrlsWithSubpath(parsedServerUrl, parsedURL)) {
return {name: teams[i].name, url: parsedServerUrl, index: i};
}
if (equalUrlsIgnoringSubpath(parsedServerUrl, parsedURL)) {
// in case the user added something on the path that doesn't really belong to the server
// there might be more than one that matches, but we can't differentiate, so last one
// is as good as any other in case there is no better match (e.g.: two subpath servers with the same origin)
// e.g.: https://community.mattermost.com/core
secondOption = {name: teams[i].name, url: parsedServerUrl, index: i};
}
}
return secondOption;
}
function getDisplayBoundaries() {
const {screen} = electron;
const displays = screen.getAllDisplays();
return displays.map((display) => {
return {
maxX: display.workArea.x + display.workArea.width,
maxY: display.workArea.y + display.workArea.height,
minX: display.workArea.x,
minY: display.workArea.y,
maxWidth: display.workArea.width,
maxHeight: display.workArea.height,
};
});
}
// next two functions are defined to clarify intent
function equalUrlsWithSubpath(url1, url2) {
return url1.origin === url2.origin && url2.pathname.toLowerCase().startsWith(url1.pathname.toLowerCase());
}
function equalUrlsIgnoringSubpath(url1, url2) {
return url1.origin.toLowerCase() === url2.origin.toLowerCase();
}
const dispatchNotification = async (title, body, silent, handleClick) => {
let permission;
const appIconURL = `file:///${remote.app.getAppPath()}/assets/appicon_48.png`;
if (Notification.permission === 'default') {
permission = await Notification.requestPermission();
} else {
permission = Notification.permission;
}
if (permission !== 'granted') {
log.error('Notifications not granted');
return null;
}
const notification = new Notification(title, {
body,
tag: body,
icon: appIconURL,
requireInteraction: false,
silent
});
notification.onclick = handleClick;
notification.onerror = () => {
log.error('Notification failed to show');
};
return notification;
};
export default {
getDomain,
isValidURL,
isValidURI,
isInternalURL,
parseURL,
getServer,
getServerInfo,
isTeamUrl,
isPluginUrl,
getDisplayBoundaries,
dispatchNotification,
getHost,
};