clean up formatting

Signed-off-by: Paul Schultz <pschultz@pobox.com>
This commit is contained in:
Paul Schultz
2023-03-08 10:13:38 -06:00
parent 34fe6c9246
commit 040b54f7e5
26 changed files with 449 additions and 417 deletions
+86 -86
View File
@@ -46,67 +46,67 @@ The permissions framework uses a new `permission-backend` plugin to accept autho
1. Add `@backstage/plugin-permission-backend` as a dependency of your Backstage backend:
```bash
# From your Backstage root directory
$ yarn add --cwd packages/backend @backstage/plugin-permission-backend
```
```bash
# From your Backstage root directory
$ yarn add --cwd packages/backend @backstage/plugin-permission-backend
```
2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything.
```typescript title="packages/backend/src/plugins/permission.ts"
import { createRouter } from '@backstage/plugin-permission-backend';
import {
AuthorizeResult,
PolicyDecision,
} from '@backstage/plugin-permission-common';
import { PermissionPolicy } from '@backstage/plugin-permission-node';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
```typescript title="packages/backend/src/plugins/permission.ts"
import { createRouter } from '@backstage/plugin-permission-backend';
import {
AuthorizeResult,
PolicyDecision,
} from '@backstage/plugin-permission-common';
import { PermissionPolicy } from '@backstage/plugin-permission-node';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
class TestPermissionPolicy implements PermissionPolicy {
async handle(): Promise<PolicyDecision> {
return { result: AuthorizeResult.ALLOW };
}
}
class TestPermissionPolicy implements PermissionPolicy {
async handle(): Promise<PolicyDecision> {
return { result: AuthorizeResult.ALLOW };
}
}
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
config: env.config,
logger: env.logger,
discovery: env.discovery,
policy: new TestPermissionPolicy(),
identity: env.identity,
});
}
```
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
config: env.config,
logger: env.logger,
discovery: env.discovery,
policy: new TestPermissionPolicy(),
identity: env.identity,
});
}
```
3. Wire up the permission policy in `packages/backend/src/index.ts`. [The index in the example backend](https://github.com/backstage/backstage/blob/master/packages/backend/src/index.ts) shows how to do this. Youll need to import the module from the previous step, create a plugin environment, and add the router to the express app:
```ts title="packages/backend/src/index.ts"
import proxy from './plugins/proxy';
import techdocs from './plugins/techdocs';
import search from './plugins/search';
/* highlight-add-next-line */
import permission from './plugins/permission';
```ts title="packages/backend/src/index.ts"
import proxy from './plugins/proxy';
import techdocs from './plugins/techdocs';
import search from './plugins/search';
/* highlight-add-next-line */
import permission from './plugins/permission';
async function main() {
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
const searchEnv = useHotMemoize(module, () => createEnv('search'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
/* highlight-add-next-line */
const permissionEnv = useHotMemoize(module, () => createEnv('permission'));
// ..
async function main() {
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
const searchEnv = useHotMemoize(module, () => createEnv('search'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
/* highlight-add-next-line */
const permissionEnv = useHotMemoize(module, () => createEnv('permission'));
// ..
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
apiRouter.use('/proxy', await proxy(proxyEnv));
apiRouter.use('/search', await search(searchEnv));
/* highlight-add-next-line */
apiRouter.use('/permission', await permission(permissionEnv));
// ..
}
```
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
apiRouter.use('/proxy', await proxy(proxyEnv));
apiRouter.use('/search', await search(searchEnv));
/* highlight-add-next-line */
apiRouter.use('/permission', await permission(permissionEnv));
// ..
}
```
### 2. Enable and test the permissions system
@@ -114,46 +114,46 @@ Now that the permission backend is running, its time to enable the permission
1. Set the property `permission.enabled` to `true` in `app-config.yaml`.
```yaml title="app-config.yaml"
permission:
enabled: true
```
```yaml title="app-config.yaml"
permission:
enabled: true
```
2. Update the PermissionPolicy in `packages/backend/src/plugins/permission.ts` to disable a permission thats easy for us to test. This policy rejects any attempt to delete a catalog entity:
```ts title="packages/backend/src/plugins/permission.ts"
import { createRouter } from '@backstage/plugin-permission-backend';
import {
AuthorizeResult,
PolicyDecision,
} from '@backstage/plugin-permission-common';
/* highlight-remove-next-line */
import { PermissionPolicy } from '@backstage/plugin-permission-node';
/* highlight-add-start */
import {
PermissionPolicy,
PolicyQuery,
} from '@backstage/plugin-permission-node';
/* highlight-add-end */
import { Router } from 'express';
import { PluginEnvironment } from '../types';
```ts title="packages/backend/src/plugins/permission.ts"
import { createRouter } from '@backstage/plugin-permission-backend';
import {
AuthorizeResult,
PolicyDecision,
} from '@backstage/plugin-permission-common';
/* highlight-remove-next-line */
import { PermissionPolicy } from '@backstage/plugin-permission-node';
/* highlight-add-start */
import {
PermissionPolicy,
PolicyQuery,
} from '@backstage/plugin-permission-node';
/* highlight-add-end */
import { Router } from 'express';
import { PluginEnvironment } from '../types';
class TestPermissionPolicy implements PermissionPolicy {
/* highlight-remove-next-line */
async handle(): Promise<PolicyDecision> {
/* highlight-add-start */
async handle(request: PolicyQuery): Promise<PolicyDecision> {
if (request.permission.name === 'catalog.entity.delete') {
return {
result: AuthorizeResult.DENY,
};
}
/* highlight-add-end */
class TestPermissionPolicy implements PermissionPolicy {
/* highlight-remove-next-line */
async handle(): Promise<PolicyDecision> {
/* highlight-add-start */
async handle(request: PolicyQuery): Promise<PolicyDecision> {
if (request.permission.name === 'catalog.entity.delete') {
return {
result: AuthorizeResult.DENY,
};
}
/* highlight-add-end */
return { result: AuthorizeResult.ALLOW };
}
}
```
return { result: AuthorizeResult.ALLOW };
}
}
```
3. Now that youve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled.
+76 -76
View File
@@ -20,99 +20,99 @@ The source code is available here:
1. Copy-paste the three folders into the plugins folder of your backstage application repository (removing the `example-` prefix from each folder) or run the following script from the root of your backstage application:
```bash
$ cd $(mktemp -d)
git clone --depth 1 --quiet --no-checkout --filter=blob:none https://github.com/backstage/backstage.git .
git checkout master -- plugins/example-todo-list/
git checkout master -- plugins/example-todo-list-backend/
git checkout master -- plugins/example-todo-list-common/
sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list/package.json
sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list-backend/package.json
sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list-common/package.json
for file in plugins/*; do mv "$file" "$OLDPWD/${file/example-todo/todo}"; done
cd -
```
```bash
$ cd $(mktemp -d)
git clone --depth 1 --quiet --no-checkout --filter=blob:none https://github.com/backstage/backstage.git .
git checkout master -- plugins/example-todo-list/
git checkout master -- plugins/example-todo-list-backend/
git checkout master -- plugins/example-todo-list-common/
sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list/package.json
sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list-backend/package.json
sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list-common/package.json
for file in plugins/*; do mv "$file" "$OLDPWD/${file/example-todo/todo}"; done
cd -
```
The `plugins` directory of your project should now include `todo-list`, `todo-list-backend`, and `todo-list-common`.
The `plugins` directory of your project should now include `todo-list`, `todo-list-backend`, and `todo-list-common`.
**Important**: if you are on **Windows**, make sure you have WSL and git installed on your machine before executing the script above.
**Important**: if you are on **Windows**, make sure you have WSL and git installed on your machine before executing the script above.
2. Add these packages as dependencies for your Backstage app:
```sh
# From your Backstage root directory
$ yarn add --cwd packages/backend @internal/plugin-todo-list-backend @internal/plugin-todo-list-common
$ yarn add --cwd packages/app @internal/plugin-todo-list
```
```sh
# From your Backstage root directory
$ yarn add --cwd packages/backend @internal/plugin-todo-list-backend @internal/plugin-todo-list-common
$ yarn add --cwd packages/app @internal/plugin-todo-list
```
3. Include the backend and frontend plugin in your application:
Create a new `packages/backend/src/plugins/todolist.ts` with the following content:
Create a new `packages/backend/src/plugins/todolist.ts` with the following content:
```typescript title="packages/backend/src/plugins/todolist.ts"
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';
import { createRouter } from '@internal/plugin-todo-list-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
```typescript title="packages/backend/src/plugins/todolist.ts"
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';
import { createRouter } from '@internal/plugin-todo-list-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
discovery,
}: PluginEnvironment): Promise<Router> {
return await createRouter({
logger,
identity: DefaultIdentityClient.create({
discovery,
issuer: await discovery.getExternalBaseUrl('auth'),
}),
});
}
```
export default async function createPlugin({
logger,
discovery,
}: PluginEnvironment): Promise<Router> {
return await createRouter({
logger,
identity: DefaultIdentityClient.create({
discovery,
issuer: await discovery.getExternalBaseUrl('auth'),
}),
});
}
```
Apply the following changes to `packages/backend/src/index.ts`:
Apply the following changes to `packages/backend/src/index.ts`:
```ts title="packages/backend/src/index.ts"
import techdocs from './plugins/techdocs';
/* highlight-add-next-line */
import todoList from './plugins/todolist';
import search from './plugins/search';
```ts title="packages/backend/src/index.ts"
import techdocs from './plugins/techdocs';
/* highlight-add-next-line */
import todoList from './plugins/todolist';
import search from './plugins/search';
async function main() {
const searchEnv = useHotMemoize(module, () => createEnv('search'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
/* highlight-add-next-line */
const todoListEnv = useHotMemoize(module, () => createEnv('todolist'));
// ..
async function main() {
const searchEnv = useHotMemoize(module, () => createEnv('search'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
/* highlight-add-next-line */
const todoListEnv = useHotMemoize(module, () => createEnv('todolist'));
// ..
apiRouter.use('/proxy', await proxy(proxyEnv));
apiRouter.use('/search', await search(searchEnv));
apiRouter.use('/permission', await permission(permissionEnv));
/* highlight-add-next-line */
apiRouter.use('/todolist', await todoList(todoListEnv));
// Add backends ABOVE this line; this 404 handler is the catch-all fallback
apiRouter.use(notFoundHandler());
// ..
}
```
apiRouter.use('/proxy', await proxy(proxyEnv));
apiRouter.use('/search', await search(searchEnv));
apiRouter.use('/permission', await permission(permissionEnv));
/* highlight-add-next-line */
apiRouter.use('/todolist', await todoList(todoListEnv));
// Add backends ABOVE this line; this 404 handler is the catch-all fallback
apiRouter.use(notFoundHandler());
// ..
}
```
Apply the following changes to `packages/app/src/App.tsx`:
Apply the following changes to `packages/app/src/App.tsx`:
```tsx title="packages/app/src/App.tsx"
/* highlight-add-next-line */
import { TodoListPage } from '@internal/plugin-todo-list';
```tsx title="packages/app/src/App.tsx"
/* highlight-add-next-line */
import { TodoListPage } from '@internal/plugin-todo-list';
const routes = (
<FlatRoutes>
<Route path="/search" element={<SearchPage />}>
{searchPage}
</Route>
<Route path="/settings" element={<UserSettingsPage />} />
{/* highlight-add-next-line */}
<Route path="/todo-list" element={<TodoListPage />} />
{/* ... */}
</FlatRoutes>
);
```
const routes = (
<FlatRoutes>
<Route path="/search" element={<SearchPage />}>
{searchPage}
</Route>
<Route path="/settings" element={<UserSettingsPage />} />
{/* highlight-add-next-line */}
<Route path="/todo-list" element={<TodoListPage />} />
{/* ... */}
</FlatRoutes>
);
```
Now if you start your application you should be able to reach the `/todo-list` page:
@@ -32,7 +32,10 @@ export const todoListUpdatePermission = createPermission({
/* highlight-remove-next-line */
export const todoListPermissions = [todoListCreatePermission];
/* highlight-add-next-line */
export const todoListPermissions = [todoListCreatePermission, todoListUpdatePermission];
export const todoListPermissions = [
todoListCreatePermission,
todoListUpdatePermission,
];
```
Notice that unlike `todoListCreatePermission`, the `todoListUpdatePermission` permission contains a `resourceType` field. This field indicates to the permission framework that this permission is intended to be authorized in the context of a resource with type `'todo-item'`. You can use whatever string you like as the resource type, as long as you use the same value consistently for each type of resource.
@@ -45,7 +48,10 @@ To start, let's edit `plugins/todo-list-backend/src/service/router.ts` in the sa
/* highlight-remove-next-line */
import { todoListCreatePermission } from '@internal/plugin-todo-list-common';
/* highlight-add-next-line */
import { todoListCreatePermission, todoListUpdatePermission } from '@internal/plugin-todo-list-common';
import {
todoListCreatePermission,
todoListUpdatePermission,
} from '@internal/plugin-todo-list-common';
router.put('/todos', async (req, res) => {
/* highlight-add-start */
@@ -14,11 +14,14 @@ router.get('/todos', async (req, res) => {
const token = IdentityClient.getBearerToken(req.header('authorization'));
/* highlight-remove-next-line */
res.json(getAll())
res.json(getAll());
/* highlight-add-start */
const items = getAll();
const decisions = await permissions.authorize(
items.map(({ id }) => ({ permission: todoListReadPermission, resourceRef: id })),
items.map(({ id }) => ({
permission: todoListReadPermission,
resourceRef: id,
})),
);
const filteredItems = decisions.filter(
@@ -64,9 +67,16 @@ export const todoListReadPermission = createPermission({
/* highlight-add-end */
/* highlight-add-next-line */
export const todoListPermissions = [todoListCreatePermission, todoListUpdatePermission];
export const todoListPermissions = [
todoListCreatePermission,
todoListUpdatePermission,
];
/* highlight-add-next-line */
export const todoListPermissions = [todoListCreatePermission, todoListUpdatePermission, todoListReadPermission];
export const todoListPermissions = [
todoListCreatePermission,
todoListUpdatePermission,
todoListReadPermission,
];
```
## Using conditional policy decisions
@@ -36,7 +36,9 @@ import { todoListCreatePermission } from '@internal/plugin-todo-list-common';
function AddTodo({ onAdd }: { onAdd: (title: string) => any }) {
const title = useRef('');
/* highlight-add-next-line */
const { loading: loadingPermission, allowed: canAddTodo } = usePermission({ permission: todoListCreatePermission });
const { loading: loadingPermission, allowed: canAddTodo } = usePermission({
permission: todoListCreatePermission,
});
return (
<>
@@ -78,7 +80,6 @@ Here we are using the [`usePermission` hook](https://backstage.io/docs/reference
It's really that simple! Let's change our policy to test the disabled button:
```ts title="packages/backend/src/plugins/permission.ts"
if (isPermission(request.permission, todoListCreatePermission)) {
return {
/* highlight-remove-next-line */
@@ -117,10 +118,7 @@ export const TodoListPage = () => {
</Grid>
{/* highlight-remove-end */}
{/* highlight-add-start */}
<RequirePermission
permission={todoListCreatePermission}
errorPage={<></>}
>
<RequirePermission permission={todoListCreatePermission} errorPage={<></>}>
<Grid item>
<AddTodo onAdd={handleAdd} />
</Grid>
@@ -129,13 +127,15 @@ export const TodoListPage = () => {
<Grid item>
<TodoList key={key} onEdit={setEdit} />
</Grid>
</Grid>
}
</Grid>;
};
function AddTodo({ onAdd }: { onAdd: (title: string) => any }) {
const title = useRef('');
/* highlight-remove-next-line */
const { loading: loadingPermission, allowed: canAddTodo } = usePermission({ permission: todoListCreatePermission });
const { loading: loadingPermission, allowed: canAddTodo } = usePermission({
permission: todoListCreatePermission,
});
return (
<>