Revert app manager protections and let identity API hang.

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Eric Peterson
2022-01-19 10:31:05 +01:00
parent f959c22787
commit 6c59abc74e
4 changed files with 10 additions and 154 deletions
@@ -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();
});
});
+1 -24
View File
@@ -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>();