Revert app manager protections and let identity API hang.
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
@@ -46,12 +46,12 @@ type CompatibilityIdentityApi = IdentityApi & {
|
||||
* and sign-in page.
|
||||
*/
|
||||
export class AppIdentityProxy implements IdentityApi {
|
||||
private target?: CompatibilityIdentityApi;
|
||||
private waitForTarget: Promise<IdentityApi>;
|
||||
private resolveTarget: (api: IdentityApi) => void = () => {};
|
||||
private target?: CompatibilityIdentityApi;
|
||||
private waitForTarget: Promise<CompatibilityIdentityApi>;
|
||||
private resolveTarget: (api: CompatibilityIdentityApi) => void = () => {};
|
||||
|
||||
constructor() {
|
||||
this.waitForTarget = new Promise<IdentityApi>(resolve => {
|
||||
this.waitForTarget = new Promise<CompatibilityIdentityApi>(resolve => {
|
||||
this.resolveTarget = resolve;
|
||||
});
|
||||
}
|
||||
@@ -89,7 +89,9 @@ private waitForTarget: Promise<IdentityApi>;
|
||||
}
|
||||
|
||||
async getBackstageIdentity(): Promise<BackstageUserIdentity> {
|
||||
const identity = await this.waitForTarget.then(target => target.getBackstageIdentity());
|
||||
const identity = await this.waitForTarget.then(target =>
|
||||
target.getBackstageIdentity(),
|
||||
);
|
||||
if (!identity.userEntityRef.match(/^.*:.*\/.*$/)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
@@ -106,12 +108,12 @@ private waitForTarget: Promise<IdentityApi>;
|
||||
}
|
||||
|
||||
async getIdToken(): Promise<string | undefined> {
|
||||
return this.waitForTarget.then((target: CompatibilityIdentityApi) => {
|
||||
return this.waitForTarget.then(target => {
|
||||
if (!target.getIdToken) {
|
||||
throw new Error('IdentityApi does not implement getIdToken');
|
||||
}
|
||||
logDeprecation('getIdToken');
|
||||
return target.getIdToken()
|
||||
return target.getIdToken();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -53,24 +53,6 @@ export const ApiProvider = (props: PropsWithChildren<ApiProviderProps>) => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as above, but does not inherit APIs from API providers further up in
|
||||
* the react tree.
|
||||
*
|
||||
* Private to (and only used in) this package.
|
||||
*/
|
||||
export const IsolatedApiProvider = (
|
||||
props: PropsWithChildren<ApiProviderProps>,
|
||||
) => {
|
||||
const { apis, children } = props;
|
||||
return (
|
||||
<ApiContext.Provider
|
||||
value={createVersionedValueMap({ 1: apis })}
|
||||
children={children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
ApiProvider.propTypes = {
|
||||
apis: PropTypes.shape({ get: PropTypes.func.isRequired }).isRequired,
|
||||
children: PropTypes.node,
|
||||
|
||||
@@ -34,9 +34,6 @@ import {
|
||||
createSubRouteRef,
|
||||
createRoutableExtension,
|
||||
analyticsApiRef,
|
||||
useApi,
|
||||
identityApiRef,
|
||||
fetchApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { AppManager } from './AppManager';
|
||||
import { AppComponents, AppIcons } from './types';
|
||||
@@ -572,106 +569,4 @@ describe('Integration Test', () => {
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
it('explodes if identityApi is directly used in a given SignInPage', async () => {
|
||||
// Sign-in page that uses APIs it oughtn't to directly!
|
||||
const OffendingSignInPage = () => {
|
||||
try {
|
||||
useApi(identityApiRef);
|
||||
return <>No problem.</>;
|
||||
} catch (e) {
|
||||
return <>{(e as any).message}</>;
|
||||
}
|
||||
};
|
||||
|
||||
const app = new AppManager({
|
||||
themes: [
|
||||
{
|
||||
id: 'light',
|
||||
title: 'Light Theme',
|
||||
variant: 'light',
|
||||
Provider: ({ children }) => <>{children}</>,
|
||||
},
|
||||
],
|
||||
icons,
|
||||
components: {
|
||||
...components,
|
||||
SignInPage: OffendingSignInPage,
|
||||
},
|
||||
configLoader: async () => [],
|
||||
});
|
||||
|
||||
const Provider = app.getProvider();
|
||||
const Router = app.getRouter();
|
||||
const { getByText } = await renderWithEffects(
|
||||
<Provider>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<ExposedComponent />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText('No implementation available for apiRef{core.identity}'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('explodes if identityApi is indirectly used in a given SignInPage', async () => {
|
||||
// Set up fetchApi with a dependency on identityApi.
|
||||
const apis = [
|
||||
createApiFactory({
|
||||
api: fetchApiRef,
|
||||
deps: { identityApiRef },
|
||||
factory: () => ({} as any),
|
||||
}),
|
||||
];
|
||||
|
||||
// Sign-in page that uses APIs it oughtn't to, but indirectly!
|
||||
const OffendingSignInPage = () => {
|
||||
try {
|
||||
useApi(fetchApiRef);
|
||||
return <>No problem.</>;
|
||||
} catch (e) {
|
||||
return <>{(e as any).message}</>;
|
||||
}
|
||||
};
|
||||
|
||||
const app = new AppManager({
|
||||
apis,
|
||||
themes: [
|
||||
{
|
||||
id: 'light',
|
||||
title: 'Light Theme',
|
||||
variant: 'light',
|
||||
Provider: ({ children }) => <>{children}</>,
|
||||
},
|
||||
],
|
||||
icons,
|
||||
components: {
|
||||
...components,
|
||||
SignInPage: OffendingSignInPage,
|
||||
},
|
||||
configLoader: async () => [],
|
||||
});
|
||||
|
||||
const Provider = app.getProvider();
|
||||
const Router = app.getRouter();
|
||||
const { getByText } = await renderWithEffects(
|
||||
<Provider>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<ExposedComponent />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText(
|
||||
'No API factory available for dependency apiRef{core.identity} of dependent apiRef{core.fetch}',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -79,7 +79,6 @@ import { AppThemeProvider } from './AppThemeProvider';
|
||||
import { defaultConfigLoader } from './defaultConfigLoader';
|
||||
import { ApiRegistry } from '../apis/system/ApiRegistry';
|
||||
import { resolveRouteBindings } from './resolveRouteBindings';
|
||||
import { IsolatedApiProvider } from '../apis/system/ApiProvider';
|
||||
|
||||
type CompatiblePlugin =
|
||||
| BackstagePlugin<any, any>
|
||||
@@ -336,14 +335,7 @@ export class AppManager implements BackstageApp {
|
||||
const [identityApi, setIdentityApi] = useState<IdentityApi>();
|
||||
|
||||
if (!identityApi) {
|
||||
// Encapsulate the sign in page component in an API provider which
|
||||
// contains all APIs except the identity API. Provides fast feedback in
|
||||
// case the identity API is accidentally required on the sign-in page.
|
||||
return (
|
||||
<IsolatedApiProvider apis={this.getIdentitylessApiHolder()}>
|
||||
<Component onSignInSuccess={setIdentityApi} />
|
||||
</IsolatedApiProvider>
|
||||
);
|
||||
return <Component onSignInSuccess={setIdentityApi} />;
|
||||
}
|
||||
|
||||
this.appIdentityProxy.setTarget(identityApi);
|
||||
@@ -481,21 +473,6 @@ export class AppManager implements BackstageApp {
|
||||
return this.apiHolder;
|
||||
}
|
||||
|
||||
private getIdentitylessApiHolder() {
|
||||
const registrySansIdentity = new ApiFactoryRegistry();
|
||||
|
||||
// Assume all APIs in the current registry are valid.
|
||||
this.apiFactoryRegistry.getAllApis().forEach(apiRef => {
|
||||
const factory = this.apiFactoryRegistry.get(apiRef);
|
||||
// Add all API factories except identity!
|
||||
if (factory && apiRef !== identityApiRef) {
|
||||
registrySansIdentity.register('default', factory);
|
||||
}
|
||||
});
|
||||
|
||||
return new ApiResolver(registrySansIdentity);
|
||||
}
|
||||
|
||||
private verifyPlugins(plugins: Iterable<CompatiblePlugin>) {
|
||||
const pluginIds = new Set<string>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user