Merge pull request #1889 from umbraco/bugfix/iframe-mode-origin

fix: the backoffice does not work with window.open
This commit is contained in:
Jacob Overgaard
2024-05-24 11:58:14 +02:00
committed by GitHub
4 changed files with 102 additions and 2 deletions

View File

@@ -18,7 +18,7 @@ import {
umbExtensionsRegistry,
} from '@umbraco-cms/backoffice/extension-registry';
import { filter, first, firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { retrieveStoredPath } from '@umbraco-cms/backoffice/utils';
import { hasOwnOpener, retrieveStoredPath } from '@umbraco-cms/backoffice/utils';
@customElement('umb-app')
export class UmbAppElement extends UmbLitElement {
@@ -77,7 +77,7 @@ export class UmbAppElement extends UmbLitElement {
// The authorization request will be completed in the active window (main or popup) and the authorization signal will be sent.
// If we are in a popup window, the storage event in UmbAuthContext will catch the signal and close the window.
// If we are in the main window, the signal will be caught right here and the user will be redirected to the root.
if (window.opener) {
if (hasOwnOpener(this.backofficePath)) {
(component as UmbAppErrorElement).errorMessage = hasCode
? this.localize.term('errors_externalLoginSuccess')
: this.localize.term('errors_externalLoginFailed');

View File

@@ -7,6 +7,7 @@ export * from './object/deep-merge.function.js';
export * from './pagination-manager/pagination.manager.js';
export * from './path/ensure-local-path.function.js';
export * from './path/ensure-path-ends-with-slash.function.js';
export * from './path/has-own-opener.function.js';
export * from './path/path-decode.function.js';
export * from './path/path-encode.function.js';
export * from './path/path-folder-name.function.js';

View File

@@ -0,0 +1,64 @@
import { expect } from '@open-wc/testing';
import { hasOwnOpener } from './has-own-opener.function.js';
describe('hasOwnOpener', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let mockWindow: any;
beforeEach(() => {
mockWindow = {
location: {
origin: 'http://localhost',
pathname: '/test',
},
};
});
it('should return false if there is no opener', () => {
expect(hasOwnOpener(undefined, mockWindow)).to.be.false;
});
it('should return false if the opener is from a different origin', () => {
mockWindow.opener = {
location: {
origin: 'https://example.com',
pathname: '/test',
},
};
expect(hasOwnOpener(undefined, mockWindow)).to.be.false;
});
it('should return true if the opener is from the same origin and no pathname is specified', () => {
mockWindow.opener = {
location: {
origin: 'http://localhost',
pathname: '/test',
},
};
expect(hasOwnOpener(undefined, mockWindow)).to.be.true;
});
it('should return false if the opener is from the same origin but has a different pathname', () => {
mockWindow.opener = {
location: {
origin: 'http://localhost',
pathname: '/different',
},
};
expect(hasOwnOpener('/test', mockWindow)).to.be.false;
});
it('should return true if the opener is from the same origin and has the same pathname', () => {
mockWindow.opener = {
location: {
origin: 'http://localhost',
pathname: '/test',
},
};
expect(hasOwnOpener('/test', mockWindow)).to.be.true;
});
});

View File

@@ -0,0 +1,35 @@
/**
* Check if the current window has an opener window with the same origin and optional pathname.
* This is useful for checking if the current window was opened by another window from within the same application.
* @remark If the current window was opened by another window, the opener window is accessible via `window.opener`.
* @remark There could still be an opener if the opener window is closed or navigated away or if the opener window is not from the same origin,
* but this function will only return `true` if the opener window is accessible and has the same origin and optional pathname.
*
* @param pathname Optional pathname to check if the opener window has the same pathname.
* @param windowLike The window-like object to use for checking the opener. Default is `window`.
* @returns `true` if the current window has an opener window with the same origin and optional pathname, otherwise `false`.
*/
export function hasOwnOpener(pathname?: string, windowLike: Window = globalThis.window): boolean {
try {
const opener = windowLike.opener;
if (!opener) {
return false;
}
const openerLocation = opener.location;
const currentLocation = windowLike.location;
if (openerLocation.origin !== currentLocation.origin) {
return false;
}
if (pathname && openerLocation.pathname !== pathname) {
return false;
}
return true;
} catch (e) {
// If there is a security error, it means that the opener is from a different origin, so we let it fall through
return false;
}
}