chore: initial plugin fix
Signed-off-by: benjdlambert <ben@blam.sh> Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
"role": "backend-plugin",
|
||||
"pluginId": "auth",
|
||||
"pluginPackages": [
|
||||
"@backstage/plugin-auth",
|
||||
"@backstage/plugin-auth-backend",
|
||||
"@backstage/plugin-auth-node",
|
||||
"@backstage/plugin-auth-react"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"role": "node-library",
|
||||
"pluginId": "auth",
|
||||
"pluginPackages": [
|
||||
"@backstage/plugin-auth",
|
||||
"@backstage/plugin-auth-backend",
|
||||
"@backstage/plugin-auth-node",
|
||||
"@backstage/plugin-auth-react"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"role": "web-library",
|
||||
"pluginId": "auth",
|
||||
"pluginPackages": [
|
||||
"@backstage/plugin-auth",
|
||||
"@backstage/plugin-auth-backend",
|
||||
"@backstage/plugin-auth-node",
|
||||
"@backstage/plugin-auth-react"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,12 @@
|
||||
# @backstage/plugin-auth
|
||||
|
||||
A Backstage frontend plugin that provides user interface components for authentication flows, specifically for OpenID Connect (OIDC) consent management.
|
||||
|
||||
## Installation
|
||||
|
||||
This plugin is designed to work with the `@backstage/plugin-auth-backend` package that provides OIDC provider functionality.
|
||||
|
||||
```bash
|
||||
# From your Backstage app directory
|
||||
yarn --cwd packages/app add @backstage/plugin-auth
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-auth
|
||||
title: '@backstage/plugin-auth'
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-frontend-plugin
|
||||
owner: auth-maintainers
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
// todo
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "@backstage/plugin-auth",
|
||||
"version": "0.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"package.json": [
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/auth"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "frontend-plugin",
|
||||
"pluginId": "auth",
|
||||
"pluginPackages": [
|
||||
"@backstage/plugin-auth",
|
||||
"@backstage/plugin-auth-backend",
|
||||
"@backstage/plugin-auth-node",
|
||||
"@backstage/plugin-auth-react"
|
||||
]
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-compat-api": "workspace:^",
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/theme": "workspace:^",
|
||||
"@material-ui/core": "^4.9.13",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"react-use": "^17.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0",
|
||||
"react-router-dom": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/dev-utils": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"msw": "^1.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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 { useCallback, useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardActions,
|
||||
Typography,
|
||||
makeStyles,
|
||||
Divider,
|
||||
} from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
||||
import CancelIcon from '@material-ui/icons/Cancel';
|
||||
import AppsIcon from '@material-ui/icons/Apps';
|
||||
import WarningIcon from '@material-ui/icons/Warning';
|
||||
import {
|
||||
Header,
|
||||
Page,
|
||||
Content,
|
||||
Progress,
|
||||
EmptyState,
|
||||
ResponseErrorPanel,
|
||||
} from '@backstage/core-components';
|
||||
import {
|
||||
alertApiRef,
|
||||
useApi,
|
||||
fetchApiRef,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { isError } from '@backstage/errors';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
authCard: {
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
marginTop: theme.spacing(4),
|
||||
},
|
||||
appHeader: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
appIcon: {
|
||||
marginRight: theme.spacing(2),
|
||||
fontSize: 40,
|
||||
},
|
||||
appName: {
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
securityWarning: {
|
||||
margin: theme.spacing(2, 0),
|
||||
},
|
||||
buttonContainer: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
gap: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
callbackUrl: {
|
||||
fontFamily: 'monospace',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
padding: theme.spacing(1),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
wordBreak: 'break-all',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
scopeList: {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
interface Session {
|
||||
id: string;
|
||||
clientName?: string;
|
||||
clientId: string;
|
||||
redirectUri: string;
|
||||
scopes?: string[];
|
||||
responseType?: string;
|
||||
state?: string;
|
||||
nonce?: string;
|
||||
codeChallenge?: string;
|
||||
codeChallengeMethod?: string;
|
||||
expiresAt?: string;
|
||||
}
|
||||
|
||||
export const ConsentPage = () => {
|
||||
const classes = useStyles();
|
||||
const { sessionId } = useParams<{ sessionId: string }>();
|
||||
const alertApi = useApi(alertApiRef);
|
||||
const fetchApi = useApi(fetchApiRef);
|
||||
const discoveryApi = useApi(discoveryApiRef);
|
||||
const [session, setSession] = useState<Session | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [completed, setCompleted] = useState<
|
||||
| {
|
||||
action: 'approve' | 'reject';
|
||||
}
|
||||
| undefined
|
||||
>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSession = async () => {
|
||||
if (!sessionId) return;
|
||||
|
||||
try {
|
||||
const baseUrl = await discoveryApi.getBaseUrl('auth');
|
||||
const response = await fetchApi.fetch(
|
||||
`${baseUrl}/v1/sessions/${sessionId}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setSession(data);
|
||||
} catch (err) {
|
||||
setError(isError(err) ? err.message : 'Failed to load consent request');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchSession();
|
||||
}, [sessionId, discoveryApi, fetchApi]);
|
||||
|
||||
const handleAction = useCallback(
|
||||
async (action: 'approve' | 'reject') => {
|
||||
if (!session) return;
|
||||
|
||||
setSubmitting(true);
|
||||
try {
|
||||
const baseUrl = await discoveryApi.getBaseUrl('auth');
|
||||
const response = await fetchApi.fetch(
|
||||
`${baseUrl}/v1/sessions/${session.id}/${action}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
setCompleted({
|
||||
action,
|
||||
});
|
||||
|
||||
if (result.redirectUrl) {
|
||||
window.location.href = result.redirectUrl;
|
||||
}
|
||||
} catch (err) {
|
||||
alertApi.post({
|
||||
message: isError(err) ? err.message : `Failed to ${action} consent`,
|
||||
severity: 'error',
|
||||
});
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
},
|
||||
[session, discoveryApi, fetchApi, alertApi],
|
||||
);
|
||||
|
||||
if (!sessionId) {
|
||||
return (
|
||||
<Page themeId="tool">
|
||||
<Header title="Authorization Error" />
|
||||
<Content>
|
||||
<EmptyState
|
||||
missing="data"
|
||||
title="Invalid Request"
|
||||
description="The consent request ID is missing or invalid."
|
||||
/>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Page themeId="tool">
|
||||
<Header title="Authorization Request" />
|
||||
<Content>
|
||||
<Box
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
minHeight={300}
|
||||
>
|
||||
<Progress />
|
||||
</Box>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
if (error ?? !session) {
|
||||
return (
|
||||
<Page themeId="tool">
|
||||
<Header title="Authorization Error" />
|
||||
<Content>
|
||||
<ResponseErrorPanel
|
||||
error={new Error(error || 'Authorization request not found')}
|
||||
/>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
if (completed) {
|
||||
return (
|
||||
<Page themeId="tool">
|
||||
<Header title="Authorization Complete" />
|
||||
<Content>
|
||||
<Card className={classes.authCard}>
|
||||
<CardContent>
|
||||
<Box textAlign="center">
|
||||
{completed.action === 'approve' ? (
|
||||
<CheckCircleIcon
|
||||
style={{ fontSize: 64, color: 'green', marginBottom: 16 }}
|
||||
/>
|
||||
) : (
|
||||
<CancelIcon
|
||||
style={{ fontSize: 64, color: 'red', marginBottom: 16 }}
|
||||
/>
|
||||
)}
|
||||
<Typography variant="h5" gutterBottom>
|
||||
{completed.action === 'approve'
|
||||
? 'Authorization Approved'
|
||||
: 'Authorization Denied'}
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary" gutterBottom>
|
||||
{completed.action === 'approve'
|
||||
? 'You have successfully authorized the application to access your Backstage account.'
|
||||
: 'You have denied the application access to your Backstage account.'}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Redirecting to the application...
|
||||
</Typography>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
const appName = session.clientName ?? session.clientId;
|
||||
|
||||
return (
|
||||
<Page themeId="tool">
|
||||
<Header title="Authorization Request" />
|
||||
<Content>
|
||||
<Card className={classes.authCard}>
|
||||
<CardContent>
|
||||
<Box className={classes.appHeader}>
|
||||
<AppsIcon className={classes.appIcon} />
|
||||
<Box>
|
||||
<Typography className={classes.appName}>{appName}</Typography>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
wants to access your Backstage account
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Alert
|
||||
severity="warning"
|
||||
icon={<WarningIcon />}
|
||||
className={classes.securityWarning}
|
||||
>
|
||||
<Typography variant="body2">
|
||||
<strong>Security Notice:</strong> By authorizing this
|
||||
application, you are granting it access to your Backstage
|
||||
account. The application will receive an access token that
|
||||
allows it to act on your behalf.
|
||||
</Typography>
|
||||
<Box mt={1}>
|
||||
<Typography variant="body2">
|
||||
<strong>Callback URL:</strong>
|
||||
</Typography>
|
||||
<Box className={classes.callbackUrl}>{session.redirectUri}</Box>
|
||||
</Box>
|
||||
</Alert>
|
||||
|
||||
<Box mt={2}>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Make sure you trust this application and recognize the callback
|
||||
URL above. Only authorize applications you trust.
|
||||
</Typography>
|
||||
</Box>
|
||||
</CardContent>
|
||||
|
||||
<CardActions className={classes.buttonContainer}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="large"
|
||||
disabled={submitting}
|
||||
onClick={() => handleAction('reject')}
|
||||
startIcon={<CancelIcon />}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="large"
|
||||
disabled={submitting}
|
||||
onClick={() => handleAction('approve')}
|
||||
startIcon={<CheckCircleIcon />}
|
||||
>
|
||||
{submitting ? 'Authorizing...' : 'Authorize'}
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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 { ConsentPage } from './ConsentPage';
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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 { Routes, Route } from 'react-router-dom';
|
||||
import { ConsentPage } from './ConsentPage';
|
||||
|
||||
/**
|
||||
* Router component for the auth plugin
|
||||
* @public
|
||||
*/
|
||||
export const Router = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/authorize/:sessionId" element={<ConsentPage />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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 { authPlugin, AuthRouter } from './plugin';
|
||||
export { rootRouteRef } from './routes';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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 { default as authPlugin } from './plugin';
|
||||
|
||||
describe('auth', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(authPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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 { compatWrapper } from '@backstage/core-compat-api';
|
||||
import {
|
||||
createFrontendPlugin,
|
||||
PageBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { rootRouteRef } from './routes';
|
||||
|
||||
export const AuthPage = PageBlueprint.make({
|
||||
params: {
|
||||
path: '/oauth2',
|
||||
routeRef: rootRouteRef,
|
||||
loader: () =>
|
||||
import('./components/Router').then(m => compatWrapper(<m.Router />)),
|
||||
},
|
||||
});
|
||||
|
||||
export default createFrontendPlugin({
|
||||
pluginId: 'auth',
|
||||
extensions: [AuthPage],
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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 { createRouteRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
export const rootRouteRef = createRouteRef();
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed 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 '@testing-library/jest-dom';
|
||||
@@ -4264,6 +4264,34 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-auth@workspace:plugins/auth":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-auth@workspace:plugins/auth"
|
||||
dependencies:
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/core-app-api": "workspace:^"
|
||||
"@backstage/core-compat-api": "workspace:^"
|
||||
"@backstage/core-components": "workspace:^"
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/dev-utils": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@backstage/theme": "workspace:^"
|
||||
"@material-ui/core": "npm:^4.9.13"
|
||||
"@material-ui/icons": "npm:^4.9.1"
|
||||
"@material-ui/lab": "npm:4.0.0-alpha.57"
|
||||
"@testing-library/jest-dom": "npm:^6.0.0"
|
||||
"@testing-library/react": "npm:^14.0.0"
|
||||
"@testing-library/user-event": "npm:^14.0.0"
|
||||
msw: "npm:^1.0.0"
|
||||
react-use: "npm:^17.2.4"
|
||||
peerDependencies:
|
||||
react: ^17.0.0
|
||||
react-router-dom: ^6.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-bitbucket-cloud-common@workspace:^, @backstage/plugin-bitbucket-cloud-common@workspace:plugins/bitbucket-cloud-common":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-bitbucket-cloud-common@workspace:plugins/bitbucket-cloud-common"
|
||||
@@ -10466,6 +10494,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material-ui/lab@npm:4.0.0-alpha.57":
|
||||
version: 4.0.0-alpha.57
|
||||
resolution: "@material-ui/lab@npm:4.0.0-alpha.57"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.4.4"
|
||||
"@material-ui/utils": "npm:^4.11.2"
|
||||
clsx: "npm:^1.0.4"
|
||||
prop-types: "npm:^15.7.2"
|
||||
react-is: "npm:^16.8.0 || ^17.0.0"
|
||||
peerDependencies:
|
||||
"@material-ui/core": ^4.9.10
|
||||
"@types/react": ^16.8.6 || ^17.0.0
|
||||
react: ^16.8.0 || ^17.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/0142df7864fd8307a577a7e98e5c198bc71225a1abfe186abb3f5bb6d15bfcf99cf64204d43ea1a9be6723907135e1773a63dc41605b84c37596a02015f3e3b4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material-ui/lab@npm:4.0.0-alpha.61, @material-ui/lab@npm:^4.0.0-alpha.57, @material-ui/lab@npm:^4.0.0-alpha.60, @material-ui/lab@npm:^4.0.0-alpha.61":
|
||||
version: 4.0.0-alpha.61
|
||||
resolution: "@material-ui/lab@npm:4.0.0-alpha.61"
|
||||
@@ -10601,7 +10650,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@material-ui/utils@npm:^4.11.3":
|
||||
"@material-ui/utils@npm:^4.11.2, @material-ui/utils@npm:^4.11.3":
|
||||
version: 4.11.3
|
||||
resolution: "@material-ui/utils@npm:4.11.3"
|
||||
dependencies:
|
||||
@@ -19487,6 +19536,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@testing-library/dom@npm:^9.0.0":
|
||||
version: 9.3.4
|
||||
resolution: "@testing-library/dom@npm:9.3.4"
|
||||
dependencies:
|
||||
"@babel/code-frame": "npm:^7.10.4"
|
||||
"@babel/runtime": "npm:^7.12.5"
|
||||
"@types/aria-query": "npm:^5.0.1"
|
||||
aria-query: "npm:5.1.3"
|
||||
chalk: "npm:^4.1.0"
|
||||
dom-accessibility-api: "npm:^0.5.9"
|
||||
lz-string: "npm:^1.5.0"
|
||||
pretty-format: "npm:^27.0.2"
|
||||
checksum: 10/510da752ea76f4a10a0a4e3a77917b0302cf03effe576cd3534cab7e796533ee2b0e9fb6fb11b911a1ebd7c70a0bb6f235bf4f816c9b82b95b8fe0cddfd10975
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@testing-library/jest-dom@npm:6.5.0":
|
||||
version: 6.5.0
|
||||
resolution: "@testing-library/jest-dom@npm:6.5.0"
|
||||
@@ -19539,6 +19604,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@testing-library/react@npm:^14.0.0":
|
||||
version: 14.3.1
|
||||
resolution: "@testing-library/react@npm:14.3.1"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.12.5"
|
||||
"@testing-library/dom": "npm:^9.0.0"
|
||||
"@types/react-dom": "npm:^18.0.0"
|
||||
peerDependencies:
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
checksum: 10/83359dcdf9eaf067839f34604e1a181cbc14fc09f3a07672403700fcc6a900c4b8054ad1114fc24b4b9f89d84e2a09e1b7c9afce2306b1d4b4c9e30eb1cb12de
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@testing-library/react@npm:^16.0.0":
|
||||
version: 16.3.0
|
||||
resolution: "@testing-library/react@npm:16.3.0"
|
||||
@@ -23941,6 +24020,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aria-query@npm:5.1.3":
|
||||
version: 5.1.3
|
||||
resolution: "aria-query@npm:5.1.3"
|
||||
dependencies:
|
||||
deep-equal: "npm:^2.0.5"
|
||||
checksum: 10/e5da608a7c4954bfece2d879342b6c218b6b207e2d9e5af270b5e38ef8418f02d122afdc948b68e32649b849a38377785252059090d66fa8081da95d1609c0d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aria-query@npm:5.3.0":
|
||||
version: 5.3.0
|
||||
resolution: "aria-query@npm:5.3.0"
|
||||
@@ -23957,7 +24045,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2":
|
||||
"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "array-buffer-byte-length@npm:1.0.2"
|
||||
dependencies:
|
||||
@@ -27717,6 +27805,32 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deep-equal@npm:^2.0.5":
|
||||
version: 2.2.3
|
||||
resolution: "deep-equal@npm:2.2.3"
|
||||
dependencies:
|
||||
array-buffer-byte-length: "npm:^1.0.0"
|
||||
call-bind: "npm:^1.0.5"
|
||||
es-get-iterator: "npm:^1.1.3"
|
||||
get-intrinsic: "npm:^1.2.2"
|
||||
is-arguments: "npm:^1.1.1"
|
||||
is-array-buffer: "npm:^3.0.2"
|
||||
is-date-object: "npm:^1.0.5"
|
||||
is-regex: "npm:^1.1.4"
|
||||
is-shared-array-buffer: "npm:^1.0.2"
|
||||
isarray: "npm:^2.0.5"
|
||||
object-is: "npm:^1.1.5"
|
||||
object-keys: "npm:^1.1.1"
|
||||
object.assign: "npm:^4.1.4"
|
||||
regexp.prototype.flags: "npm:^1.5.1"
|
||||
side-channel: "npm:^1.0.4"
|
||||
which-boxed-primitive: "npm:^1.0.2"
|
||||
which-collection: "npm:^1.0.1"
|
||||
which-typed-array: "npm:^1.1.13"
|
||||
checksum: 10/1ce49d0b71d0f14d8ef991a742665eccd488dfc9b3cada069d4d7a86291e591c92d2589c832811dea182b4015736b210acaaebce6184be356c1060d176f5a05f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deep-equal@npm:~1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "deep-equal@npm:1.0.1"
|
||||
@@ -28925,7 +29039,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-get-iterator@npm:^1.0.2":
|
||||
"es-get-iterator@npm:^1.0.2, es-get-iterator@npm:^1.1.3":
|
||||
version: 1.1.3
|
||||
resolution: "es-get-iterator@npm:1.1.3"
|
||||
dependencies:
|
||||
@@ -31522,7 +31636,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0":
|
||||
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0":
|
||||
version: 1.3.0
|
||||
resolution: "get-intrinsic@npm:1.3.0"
|
||||
dependencies:
|
||||
@@ -33455,7 +33569,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5":
|
||||
"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5":
|
||||
version: 3.0.5
|
||||
resolution: "is-array-buffer@npm:3.0.5"
|
||||
dependencies:
|
||||
@@ -33928,7 +34042,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-regex@npm:^1.2.1":
|
||||
"is-regex@npm:^1.1.4, is-regex@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "is-regex@npm:1.2.1"
|
||||
dependencies:
|
||||
@@ -33977,7 +34091,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-shared-array-buffer@npm:^1.0.4":
|
||||
"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "is-shared-array-buffer@npm:1.0.4"
|
||||
dependencies:
|
||||
@@ -43716,7 +43830,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"regexp.prototype.flags@npm:^1.5.3":
|
||||
"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.3":
|
||||
version: 1.5.4
|
||||
resolution: "regexp.prototype.flags@npm:1.5.4"
|
||||
dependencies:
|
||||
@@ -45220,7 +45334,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"side-channel@npm:^1.0.6, side-channel@npm:^1.1.0":
|
||||
"side-channel@npm:^1.0.4, side-channel@npm:^1.0.6, side-channel@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "side-channel@npm:1.1.0"
|
||||
dependencies:
|
||||
@@ -49387,7 +49501,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1":
|
||||
"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "which-boxed-primitive@npm:1.1.1"
|
||||
dependencies:
|
||||
@@ -49421,7 +49535,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-collection@npm:^1.0.2":
|
||||
"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "which-collection@npm:1.0.2"
|
||||
dependencies:
|
||||
@@ -49443,7 +49557,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18, which-typed-array@npm:^1.1.2":
|
||||
"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18, which-typed-array@npm:^1.1.2":
|
||||
version: 1.1.19
|
||||
resolution: "which-typed-array@npm:1.1.19"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user