Merge pull request #1889 from umbraco/bugfix/iframe-mode-origin
fix: the backoffice does not work with window.open
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user