parent
14cd8e21f8
commit
f84ac08225
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export default function(kibana) {
|
||||
return new kibana.Plugin({
|
||||
uiExports: {
|
||||
app: {
|
||||
require: ['kibana'],
|
||||
title: 'Redirecting',
|
||||
id: 'stateSessionStorageRedirect',
|
||||
main: 'plugins/state_session_storage_redirect',
|
||||
hidden: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"name": "state_session_storage_redirect",
|
||||
"version": "kibana",
|
||||
"description": "When using the state:storeInSessionStorage setting with the short-urls, we need some way to get the full URL's hashed states into sessionStorage, this app will grab the URL from the injected state and and put the URL hashed states into sessionStorage before redirecting the user."
|
||||
}
|
|
@ -36,7 +36,6 @@ import * as Plugins from './plugins';
|
|||
import { indexPatternsMixin } from './index_patterns';
|
||||
import { savedObjectsMixin } from './saved_objects/saved_objects_mixin';
|
||||
import { capabilitiesMixin } from './capabilities';
|
||||
import { urlShorteningMixin } from './url_shortening';
|
||||
import { serverExtensionsMixin } from './server_extensions';
|
||||
import { uiMixin } from '../ui';
|
||||
import { sassMixin } from './sass';
|
||||
|
@ -123,9 +122,6 @@ export default class KbnServer {
|
|||
// setup capabilities routes
|
||||
capabilitiesMixin,
|
||||
|
||||
// setup routes for short urls
|
||||
urlShorteningMixin,
|
||||
|
||||
// ensure that all bundles are built, or that the
|
||||
// watch bundle server is running
|
||||
optimizeMixin,
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export { urlShorteningMixin } from './url_shortening_mixin';
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { parse } from 'url';
|
||||
import { trim } from 'lodash';
|
||||
import Boom from 'boom';
|
||||
|
||||
export function shortUrlAssertValid(url) {
|
||||
const { protocol, hostname, pathname } = parse(url);
|
||||
|
||||
if (protocol) {
|
||||
throw Boom.notAcceptable(`Short url targets cannot have a protocol, found "${protocol}"`);
|
||||
}
|
||||
|
||||
if (hostname) {
|
||||
throw Boom.notAcceptable(`Short url targets cannot have a hostname, found "${hostname}"`);
|
||||
}
|
||||
|
||||
const pathnameParts = trim(pathname, '/').split('/');
|
||||
if (pathnameParts.length !== 2) {
|
||||
throw Boom.notAcceptable(
|
||||
`Short url target path must be in the format "/app/{{appId}}", found "${pathname}"`
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { shortUrlAssertValid } from './short_url_assert_valid';
|
||||
|
||||
describe('shortUrlAssertValid()', () => {
|
||||
const invalid = [
|
||||
['protocol', 'http://localhost:5601/app/kibana'],
|
||||
['protocol', 'https://localhost:5601/app/kibana'],
|
||||
['protocol', 'mailto:foo@bar.net'],
|
||||
['protocol', 'javascript:alert("hi")'], // eslint-disable-line no-script-url
|
||||
['hostname', 'localhost/app/kibana'],
|
||||
['hostname and port', 'local.host:5601/app/kibana'],
|
||||
['hostname and auth', 'user:pass@localhost.net/app/kibana'],
|
||||
['path traversal', '/app/../../not-kibana'],
|
||||
['deep path', '/app/kibana/foo'],
|
||||
['deep path', '/app/kibana/foo/bar'],
|
||||
['base path', '/base/app/kibana'],
|
||||
];
|
||||
|
||||
invalid.forEach(([desc, url]) => {
|
||||
it(`fails when url has ${desc}`, () => {
|
||||
try {
|
||||
shortUrlAssertValid(url);
|
||||
throw new Error(`expected assertion to throw`);
|
||||
} catch (err) {
|
||||
if (!err || !err.isBoom) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const valid = [
|
||||
'/app/kibana',
|
||||
'/app/monitoring#angular/route',
|
||||
'/app/text#document-id',
|
||||
'/app/some?with=query',
|
||||
'/app/some?with=query#and-a-hash',
|
||||
];
|
||||
|
||||
valid.forEach(url => {
|
||||
it(`allows ${url}`, () => {
|
||||
shortUrlAssertValid(url);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import Boom from 'boom';
|
||||
|
||||
export function handleShortUrlError(error) {
|
||||
return Boom.boomify(error, {
|
||||
statusCode: error.statusCode || error.status || 500,
|
||||
});
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { handleShortUrlError } from './short_url_error';
|
||||
|
||||
function createErrorWithStatus(status) {
|
||||
const error = new Error();
|
||||
error.status = status;
|
||||
return error;
|
||||
}
|
||||
|
||||
function createErrorWithStatusCode(statusCode) {
|
||||
const error = new Error();
|
||||
error.statusCode = statusCode;
|
||||
return error;
|
||||
}
|
||||
|
||||
describe('handleShortUrlError()', () => {
|
||||
const caughtErrorsWithStatus = [
|
||||
createErrorWithStatus(401),
|
||||
createErrorWithStatus(403),
|
||||
createErrorWithStatus(404),
|
||||
];
|
||||
|
||||
const caughtErrorsWithStatusCode = [
|
||||
createErrorWithStatusCode(401),
|
||||
createErrorWithStatusCode(403),
|
||||
createErrorWithStatusCode(404),
|
||||
];
|
||||
|
||||
const uncaughtErrors = [new Error(), createErrorWithStatus(500), createErrorWithStatusCode(500)];
|
||||
|
||||
caughtErrorsWithStatus.forEach(err => {
|
||||
it(`should handle errors with status of ${err.status}`, function() {
|
||||
expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(err.status);
|
||||
});
|
||||
});
|
||||
|
||||
caughtErrorsWithStatusCode.forEach(err => {
|
||||
it(`should handle errors with statusCode of ${err.statusCode}`, function() {
|
||||
expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(err.statusCode);
|
||||
});
|
||||
});
|
||||
|
||||
uncaughtErrors.forEach(err => {
|
||||
it(`should not handle unknown errors`, function() {
|
||||
expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(500);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
|
||||
export function shortUrlLookupProvider(server) {
|
||||
async function updateMetadata(doc, req) {
|
||||
try {
|
||||
await req.getSavedObjectsClient().update('url', doc.id, {
|
||||
accessDate: new Date(),
|
||||
accessCount: get(doc, 'attributes.accessCount', 0) + 1,
|
||||
});
|
||||
} catch (err) {
|
||||
server.log('Warning: Error updating url metadata', err);
|
||||
//swallow errors. It isn't critical if there is no update.
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
async getUrl(id, req) {
|
||||
const doc = await req.getSavedObjectsClient().get('url', id);
|
||||
updateMetadata(doc, req);
|
||||
|
||||
return doc.attributes.url;
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import sinon from 'sinon';
|
||||
import { shortUrlLookupProvider } from './short_url_lookup';
|
||||
import { SavedObjectsClient } from '../../../../../core/server';
|
||||
|
||||
describe('shortUrlLookupProvider', () => {
|
||||
const ID = 'bf00ad16941fc51420f91a93428b27a0';
|
||||
const TYPE = 'url';
|
||||
const URL = 'http://elastic.co';
|
||||
const server = { log: sinon.stub() };
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
let savedObjectsClient;
|
||||
let req;
|
||||
let shortUrl;
|
||||
|
||||
beforeEach(() => {
|
||||
savedObjectsClient = {
|
||||
get: sandbox.stub(),
|
||||
create: sandbox.stub().returns(Promise.resolve({ id: ID })),
|
||||
update: sandbox.stub(),
|
||||
errors: SavedObjectsClient.errors,
|
||||
};
|
||||
|
||||
req = { getSavedObjectsClient: () => savedObjectsClient };
|
||||
shortUrl = shortUrlLookupProvider(server);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('getUrl', () => {
|
||||
beforeEach(() => {
|
||||
const attributes = { accessCount: 2, url: URL };
|
||||
savedObjectsClient.get.returns({ id: ID, attributes });
|
||||
});
|
||||
|
||||
it('provides the ID to savedObjectsClient', async () => {
|
||||
await shortUrl.getUrl(ID, req);
|
||||
|
||||
sinon.assert.calledOnce(savedObjectsClient.get);
|
||||
const [type, id] = savedObjectsClient.get.getCall(0).args;
|
||||
|
||||
expect(type).toEqual(TYPE);
|
||||
expect(id).toEqual(ID);
|
||||
});
|
||||
|
||||
it('returns the url', async () => {
|
||||
const response = await shortUrl.getUrl(ID, req);
|
||||
expect(response).toEqual(URL);
|
||||
});
|
||||
|
||||
it('increments accessCount', async () => {
|
||||
await shortUrl.getUrl(ID, req);
|
||||
|
||||
sinon.assert.calledOnce(savedObjectsClient.update);
|
||||
const [type, id, attributes] = savedObjectsClient.update.getCall(0).args;
|
||||
|
||||
expect(type).toEqual(TYPE);
|
||||
expect(id).toEqual(ID);
|
||||
expect(Object.keys(attributes).sort()).toEqual(['accessCount', 'accessDate']);
|
||||
expect(attributes.accessCount).toEqual(3);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { createRoutes } from './routes/create_routes';
|
||||
|
||||
export function urlShorteningMixin(kbnServer, server) {
|
||||
createRoutes(server);
|
||||
}
|
|
@ -23,7 +23,7 @@ import ReactDOM from 'react-dom';
|
|||
import React from 'react';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public';
|
||||
import { NewsfeedPluginInjectedConfig } from '../types';
|
||||
import { FetchResult, NewsfeedPluginInjectedConfig } from '../types';
|
||||
import { NewsfeedNavButton, NewsfeedApiFetchResult } from './components/newsfeed_header_nav_button';
|
||||
import { getApi } from './lib/api';
|
||||
|
||||
|
@ -54,10 +54,14 @@ export class NewsfeedPublicPlugin implements Plugin<Setup, Start> {
|
|||
|
||||
private fetchNewsfeed(core: CoreStart) {
|
||||
const { http, injectedMetadata } = core;
|
||||
const config = injectedMetadata.getInjectedVar(
|
||||
'newsfeed'
|
||||
) as NewsfeedPluginInjectedConfig['newsfeed'];
|
||||
const config = injectedMetadata.getInjectedVar('newsfeed') as
|
||||
| NewsfeedPluginInjectedConfig['newsfeed']
|
||||
| undefined;
|
||||
|
||||
if (!config) {
|
||||
// running in new platform, injected metadata not available
|
||||
return new Rx.Observable<void | FetchResult | null>();
|
||||
}
|
||||
return getApi(http, config, this.kibanaVersion).pipe(
|
||||
takeUntil(this.stop$), // stop the interval when stop method is called
|
||||
catchError(() => Rx.of(null)) // do not throw error
|
||||
|
|
|
@ -17,11 +17,15 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { shortUrlLookupProvider } from './lib/short_url_lookup';
|
||||
import { createGotoRoute } from './goto';
|
||||
export const GOTO_PREFIX = '/goto';
|
||||
|
||||
export function createRoutes(server) {
|
||||
const shortUrlLookup = shortUrlLookupProvider(server);
|
||||
export const getUrlIdFromGotoRoute = (path: string) =>
|
||||
path.match(new RegExp(`${GOTO_PREFIX}/(.*)$`))?.[1];
|
||||
|
||||
server.route(createGotoRoute({ server, shortUrlLookup }));
|
||||
}
|
||||
export const getGotoPath = (urlId: string) => `${GOTO_PREFIX}/${urlId}`;
|
||||
|
||||
export const GETTER_PREFIX = '/api/short_url';
|
||||
|
||||
export const getUrlPath = (urlId: string) => `${GETTER_PREFIX}/${urlId}`;
|
||||
|
||||
export const CREATE_PATH = '/api/shorten_url';
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import url from 'url';
|
||||
import { HttpStart } from 'kibana/public';
|
||||
import { CREATE_PATH, getGotoPath } from '../../common/short_url_routes';
|
||||
|
||||
export async function shortenUrl(
|
||||
absoluteUrl: string,
|
||||
|
@ -34,10 +35,10 @@ export async function shortenUrl(
|
|||
|
||||
const body = JSON.stringify({ url: relativeUrl });
|
||||
|
||||
const resp = await post('/api/shorten_url', { body });
|
||||
const resp = await post(CREATE_PATH, { body });
|
||||
return url.format({
|
||||
protocol: parsedUrl.protocol,
|
||||
host: parsedUrl.host,
|
||||
pathname: `${basePath}/goto/${resp.urlId}`,
|
||||
pathname: `${basePath}${getGotoPath(resp.urlId)}`,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import { registryMock, managerMock } from './plugin.test.mocks';
|
||||
import { SharePlugin } from './plugin';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { coreMock } from '../../../core/public/mocks';
|
||||
|
||||
describe('SharePlugin', () => {
|
||||
beforeEach(() => {
|
||||
|
@ -30,16 +31,28 @@ describe('SharePlugin', () => {
|
|||
|
||||
describe('setup', () => {
|
||||
test('wires up and returns registry', async () => {
|
||||
const setup = await new SharePlugin().setup();
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const setup = await new SharePlugin().setup(coreSetup);
|
||||
expect(registryMock.setup).toHaveBeenCalledWith();
|
||||
expect(setup.register).toBeDefined();
|
||||
});
|
||||
|
||||
test('registers redirect app', async () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
await new SharePlugin().setup(coreSetup);
|
||||
expect(coreSetup.application.register).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: 'short_url_redirect',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
test('wires up and returns show function, but not registry', async () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const service = new SharePlugin();
|
||||
await service.setup();
|
||||
await service.setup(coreSetup);
|
||||
const start = await service.start({} as CoreStart);
|
||||
expect(registryMock.start).toHaveBeenCalled();
|
||||
expect(managerMock.start).toHaveBeenCalledWith(
|
||||
|
|
|
@ -17,15 +17,17 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreStart, Plugin } from 'src/core/public';
|
||||
import { CoreSetup, CoreStart, Plugin } from 'src/core/public';
|
||||
import { ShareMenuManager, ShareMenuManagerStart } from './services';
|
||||
import { ShareMenuRegistry, ShareMenuRegistrySetup } from './services';
|
||||
import { createShortUrlRedirectApp } from './services/short_url_redirect_app';
|
||||
|
||||
export class SharePlugin implements Plugin<SharePluginSetup, SharePluginStart> {
|
||||
private readonly shareMenuRegistry = new ShareMenuRegistry();
|
||||
private readonly shareContextMenu = new ShareMenuManager();
|
||||
|
||||
public async setup() {
|
||||
public async setup(core: CoreSetup) {
|
||||
core.application.register(createShortUrlRedirectApp(core, window.location));
|
||||
return {
|
||||
...this.shareMenuRegistry.setup(),
|
||||
};
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { createShortUrlRedirectApp } from './short_url_redirect_app';
|
||||
import { coreMock } from '../../../../core/public/mocks';
|
||||
import { hashUrl } from '../../../kibana_utils/public';
|
||||
|
||||
jest.mock('../../../kibana_utils/public', () => ({ hashUrl: jest.fn(x => `${x}/hashed`) }));
|
||||
|
||||
describe('short_url_redirect_app', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should fetch url and redirect to hashed version', async () => {
|
||||
const coreSetup = coreMock.createSetup({ basePath: 'base' });
|
||||
coreSetup.http.get.mockResolvedValueOnce({ url: '/app/abc' });
|
||||
const locationMock = { pathname: '/base/goto/12345', href: '' } as Location;
|
||||
|
||||
const { mount } = createShortUrlRedirectApp(coreSetup, locationMock);
|
||||
await mount();
|
||||
|
||||
// check for fetching the complete URL
|
||||
expect(coreSetup.http.get).toHaveBeenCalledWith('/api/short_url/12345');
|
||||
// check for hashing the URL returned from the server
|
||||
expect(hashUrl).toHaveBeenCalledWith('/app/abc');
|
||||
// check for redirecting to the prepended path
|
||||
expect(locationMock.href).toEqual('base/app/abc/hashed');
|
||||
});
|
||||
});
|
|
@ -17,24 +17,29 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { hashUrl } from '../../../../plugins/kibana_utils/public';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { fatalError } from 'ui/notify';
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { getUrlIdFromGotoRoute, getUrlPath, GOTO_PREFIX } from '../../common/short_url_routes';
|
||||
import { hashUrl } from '../../../kibana_utils/public';
|
||||
|
||||
uiRoutes.enable();
|
||||
uiRoutes.when('/', {
|
||||
resolve: {
|
||||
url: function(AppState, globalState, $window) {
|
||||
const redirectUrl = chrome.getInjected('redirectUrl');
|
||||
try {
|
||||
const hashedUrl = hashUrl(redirectUrl);
|
||||
const url = chrome.addBasePath(hashedUrl);
|
||||
export const createShortUrlRedirectApp = (core: CoreSetup, location: Location) => ({
|
||||
id: 'short_url_redirect',
|
||||
appRoute: GOTO_PREFIX,
|
||||
chromeless: true,
|
||||
title: 'Short URL Redirect',
|
||||
async mount() {
|
||||
const urlId = getUrlIdFromGotoRoute(location.pathname);
|
||||
|
||||
$window.location = url;
|
||||
} catch (e) {
|
||||
fatalError(e);
|
||||
}
|
||||
},
|
||||
if (!urlId) {
|
||||
throw new Error('Url id not present in path');
|
||||
}
|
||||
|
||||
const response = await core.http.get<{ url: string }>(getUrlPath(urlId));
|
||||
const redirectUrl = response.url;
|
||||
const hashedUrl = hashUrl(redirectUrl);
|
||||
const url = core.http.basePath.prepend(hashedUrl);
|
||||
|
||||
location.href = url;
|
||||
|
||||
return () => {};
|
||||
},
|
||||
});
|
|
@ -22,11 +22,13 @@ import { CoreSetup, Logger } from 'kibana/server';
|
|||
import { shortUrlLookupProvider } from './lib/short_url_lookup';
|
||||
import { createGotoRoute } from './goto';
|
||||
import { createShortenUrlRoute } from './shorten_url';
|
||||
import { createGetterRoute } from './get';
|
||||
|
||||
export function createRoutes({ http }: CoreSetup, logger: Logger) {
|
||||
const shortUrlLookup = shortUrlLookupProvider({ logger });
|
||||
const router = http.createRouter();
|
||||
|
||||
createGotoRoute({ router, shortUrlLookup, http });
|
||||
createGetterRoute({ router, shortUrlLookup, http });
|
||||
createShortenUrlRoute({ router, shortUrlLookup });
|
||||
}
|
||||
|
|
|
@ -17,23 +17,40 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { handleShortUrlError } from './lib/short_url_error';
|
||||
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
|
||||
import { CoreSetup, IRouter } from 'kibana/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
export const createGotoRoute = ({ server, shortUrlLookup }) => ({
|
||||
method: 'GET',
|
||||
path: '/goto_LP/{urlId}',
|
||||
handler: async function(request, h) {
|
||||
try {
|
||||
const url = await shortUrlLookup.getUrl(request.params.urlId, request);
|
||||
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
|
||||
import { ShortUrlLookupService } from './lib/short_url_lookup';
|
||||
import { getUrlPath } from '../../common/short_url_routes';
|
||||
|
||||
export const createGetterRoute = ({
|
||||
router,
|
||||
shortUrlLookup,
|
||||
http,
|
||||
}: {
|
||||
router: IRouter;
|
||||
shortUrlLookup: ShortUrlLookupService;
|
||||
http: CoreSetup['http'];
|
||||
}) => {
|
||||
router.get(
|
||||
{
|
||||
path: getUrlPath('{urlId}'),
|
||||
validate: {
|
||||
params: schema.object({ urlId: schema.string() }),
|
||||
},
|
||||
},
|
||||
router.handleLegacyErrors(async function(context, request, response) {
|
||||
const url = await shortUrlLookup.getUrl(request.params.urlId, {
|
||||
savedObjects: context.core.savedObjects.client,
|
||||
});
|
||||
shortUrlAssertValid(url);
|
||||
|
||||
const app = server.getHiddenUiAppById('stateSessionStorageRedirect');
|
||||
return h.renderApp(app, {
|
||||
redirectUrl: url,
|
||||
return response.ok({
|
||||
body: {
|
||||
url,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw handleShortUrlError(err);
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
|
@ -22,6 +22,7 @@ import { schema } from '@kbn/config-schema';
|
|||
|
||||
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
|
||||
import { ShortUrlLookupService } from './lib/short_url_lookup';
|
||||
import { getGotoPath } from '../../common/short_url_routes';
|
||||
|
||||
export const createGotoRoute = ({
|
||||
router,
|
||||
|
@ -34,7 +35,7 @@ export const createGotoRoute = ({
|
|||
}) => {
|
||||
router.get(
|
||||
{
|
||||
path: '/goto/{urlId}',
|
||||
path: getGotoPath('{urlId}'),
|
||||
validate: {
|
||||
params: schema.object({ urlId: schema.string() }),
|
||||
},
|
||||
|
@ -54,10 +55,13 @@ export const createGotoRoute = ({
|
|||
},
|
||||
});
|
||||
}
|
||||
return response.redirected({
|
||||
const body = await context.core.rendering.render();
|
||||
|
||||
return response.ok({
|
||||
headers: {
|
||||
location: http.basePath.prepend('/goto_LP/' + request.params.urlId),
|
||||
'content-security-policy': http.csp.header,
|
||||
},
|
||||
body,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
@ -22,6 +22,7 @@ import { schema } from '@kbn/config-schema';
|
|||
|
||||
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
|
||||
import { ShortUrlLookupService } from './lib/short_url_lookup';
|
||||
import { CREATE_PATH } from '../../common/short_url_routes';
|
||||
|
||||
export const createShortenUrlRoute = ({
|
||||
shortUrlLookup,
|
||||
|
@ -32,7 +33,7 @@ export const createShortenUrlRoute = ({
|
|||
}) => {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/shorten_url',
|
||||
path: CREATE_PATH,
|
||||
validate: {
|
||||
body: schema.object({ url: schema.string() }),
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue