Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common': major
'@red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend': major
'@red-hat-developer-hub/backstage-plugin-intelligent-assistant': major
---

Consolidate Intelligent Assistant RBAC permissions into four feature-linked sets: `intelligent-assistant.chat`, `intelligent-assistant.notebooks`, `intelligent-assistant.mcp.tools`, and `intelligent-assistant.skills`. Update backend routes, frontend permission checks, example RBAC policies, and documentation to use the new permission names and exported constants.
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,15 @@ All nested keys (`servicePort`, `systemPrompt`, `prompts`, `mcpServers`, `notebo

Update permission names in your `rbac-policy.csv`:

| Before | After |
| -------------------------- | ---------------------------------------- |
| `lightspeed.chat.read` | `intelligent-assistant.chat.access` |
| `lightspeed.chat.create` | `intelligent-assistant.chat.use` |
| `lightspeed.chat.delete` | `intelligent-assistant.chat.manage` |
| `lightspeed.chat.update` | `intelligent-assistant.chat.manage` |
| `lightspeed.notebooks.use` | `intelligent-assistant.notebooks.use` |
| | `intelligent-assistant.notebooks.manage` |
| `lightspeed.mcp.read` | `mcp.tools.use` |
| `lightspeed.mcp.manage` | `mcp.tools.manage` |
| Before | After |
Comment thread
Jdubrick marked this conversation as resolved.
| -------------------------- | --------------------------------- |
| `lightspeed.chat.read` | `intelligent-assistant.chat` |
| `lightspeed.chat.create` | `intelligent-assistant.chat` |
| `lightspeed.chat.delete` | `intelligent-assistant.chat` |
| `lightspeed.chat.update` | `intelligent-assistant.chat` |
| `lightspeed.notebooks.use` | `intelligent-assistant.notebooks` |
| `lightspeed.mcp.read` | `intelligent-assistant.mcp.tools` |
| `lightspeed.mcp.manage` | `intelligent-assistant.mcp.tools` |

#### 5. OFS dynamic plugin configuration

Expand Down Expand Up @@ -333,17 +332,16 @@ The Intelligent Assistant Backend plugin has support for the permission framewor
- When [RBAC permission](https://github.com/backstage/community-plugins/tree/main/workspaces/rbac/plugins/rbac-backend#installation) framework is enabled, for non-admin users to access intelligent-assistant backend API, the role associated with your user should have the following permission policies associated with it. Add the following in your permission policies configuration file named `rbac-policy.csv`:

```CSV
p, role:default/team_a, intelligent-assistant.chat.access, use, allow
p, role:default/team_a, intelligent-assistant.chat.use, use, allow
p, role:default/team_a, intelligent-assistant.chat.manage, use, allow
p, role:default/team_a, intelligent-assistant.chat, use, allow

# Required for Notebooks feature (if enabled)
p, role:default/team_a, intelligent-assistant.notebooks.use, use, allow
p, role:default/team_a, intelligent-assistant.notebooks.manage, use, allow
p, role:default/team_a, intelligent-assistant.notebooks, use, allow

# Required for MCP server management (if configured)
p, role:default/team_a, mcp.tools.use, use, allow
p, role:default/team_a, mcp.tools.manage, use, allow
p, role:default/team_a, intelligent-assistant.mcp.tools, use, allow

# Required for Skills feature (if enabled)
p, role:default/team_a, intelligent-assistant.skills, use, allow

g, user:default/<your-user-name>, role:default/team_a

Expand Down Expand Up @@ -456,9 +454,7 @@ When enabled, Notebooks exposes the following REST API endpoints:
**Notes**:

- All endpoints require authentication (user context is automatically provided by Backstage)
- All `/v1/*` endpoints require notebooks permissions:
- `intelligent-assistant.notebooks.use` for list/read/create session, upload document, and query endpoints
- `intelligent-assistant.notebooks.manage` for update/delete session and document endpoints
- All `/v1/*` endpoints require the `intelligent-assistant.notebooks` permission
- Document endpoints verify session ownership before allowing operations
- `documentId` in paths is the document title (URL-encoded for special characters)

Expand All @@ -467,8 +463,7 @@ When enabled, Notebooks exposes the following REST API endpoints:
When RBAC is enabled, users need the following permissions to use Notebooks:

```CSV
p, role:default/team_a, intelligent-assistant.notebooks.use, use, allow
p, role:default/team_a, intelligent-assistant.notebooks.manage, use, allow
p, role:default/team_a, intelligent-assistant.notebooks, use, allow

g, user:default/<your-user-name>, role:default/team_a
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ import type { BasicPermission } from '@backstage/plugin-permission-common';

import express, { Router } from 'express';

import {
iaNotebooksManagePermission,
iaNotebooksUsePermission,
} from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';
import { iaNotebooksPermission } from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';

import { Readable, Transform } from 'stream';

Expand Down Expand Up @@ -280,7 +277,7 @@ export async function createNotebooksRouter(
notebooksRouter.post(
'/v1/sessions',
generalRateLimiter,
requirePermission(iaNotebooksUsePermission),
requirePermission(iaNotebooksPermission),
withAuth(async (req, res, userId) => {
const { name, description, metadata } = req.body;
if (!name) {
Expand All @@ -300,7 +297,7 @@ export async function createNotebooksRouter(
notebooksRouter.get(
'/v1/sessions',
generalRateLimiter,
requirePermission(iaNotebooksUsePermission),
requirePermission(iaNotebooksPermission),
withAuth(async (_req, res, userId) => {
const sessions = await sessionService.listSessions(userId);
res.json(createSessionListResponse(sessions));
Expand All @@ -310,7 +307,7 @@ export async function createNotebooksRouter(
notebooksRouter.get(
'/v1/sessions/:sessionId',
generalRateLimiter,
requirePermission(iaNotebooksUsePermission),
requirePermission(iaNotebooksPermission),
withAuth(async (req, res, userId) => {
const { sessionId } = req.params;
const session = await sessionService.readSession(sessionId, userId);
Expand All @@ -323,7 +320,7 @@ export async function createNotebooksRouter(
notebooksRouter.put(
'/v1/sessions/:sessionId',
generalRateLimiter,
requirePermission(iaNotebooksManagePermission),
requirePermission(iaNotebooksPermission),
withAuth(async (req, res, userId) => {
const { sessionId } = req.params;
const { name, description, metadata } = req.body;
Expand All @@ -341,7 +338,7 @@ export async function createNotebooksRouter(
notebooksRouter.delete(
'/v1/sessions/:sessionId',
generalRateLimiter,
requirePermission(iaNotebooksManagePermission),
requirePermission(iaNotebooksPermission),
withAuth(async (req, res, userId) => {
const { sessionId } = req.params;
await sessionService.deleteSession(sessionId, userId);
Expand All @@ -357,7 +354,7 @@ export async function createNotebooksRouter(
notebooksRouter.get(
'/v1/sessions/:sessionId/documents',
generalRateLimiter,
requirePermission(iaNotebooksUsePermission),
requirePermission(iaNotebooksPermission),
requireSessionOwnership(),
withAuth(async (req, res) => {
const { sessionId } = req.params;
Expand All @@ -373,7 +370,7 @@ export async function createNotebooksRouter(
notebooksRouter.put(
'/v1/sessions/:sessionId/documents',
expensiveRateLimiter,
requirePermission(iaNotebooksUsePermission),
requirePermission(iaNotebooksPermission),
upload.single('file') as any,
withAuth(async (req, res, userId) => {
const { sessionId } = req.params;
Expand Down Expand Up @@ -432,7 +429,7 @@ export async function createNotebooksRouter(
notebooksRouter.get(
'/v1/sessions/:sessionId/documents/:documentId/status',
generalRateLimiter,
requirePermission(iaNotebooksUsePermission),
requirePermission(iaNotebooksPermission),
requireSessionOwnership(),
withAuth(async (req, res) => {
const { sessionId, documentId } = req.params;
Expand All @@ -452,7 +449,7 @@ export async function createNotebooksRouter(
notebooksRouter.patch(
'/v1/sessions/:sessionId/documents/:documentId',
generalRateLimiter,
requirePermission(iaNotebooksManagePermission),
requirePermission(iaNotebooksPermission),
requireSessionOwnership(),
withAuth(async (req, res) => {
const { sessionId, documentId } = req.params;
Expand Down Expand Up @@ -489,7 +486,7 @@ export async function createNotebooksRouter(
notebooksRouter.delete(
'/v1/sessions/:sessionId/documents/:documentId',
generalRateLimiter,
requirePermission(iaNotebooksManagePermission),
requirePermission(iaNotebooksPermission),
requireSessionOwnership(),
withAuth(async (req, res) => {
const { sessionId, documentId } = req.params;
Expand All @@ -515,7 +512,7 @@ export async function createNotebooksRouter(
notebooksRouter.post(
'/v1/sessions/:sessionId/query',
expensiveRateLimiter,
requirePermission(iaNotebooksUsePermission),
requirePermission(iaNotebooksPermission),
express.json({ limit: EXPRESS_JSON_BODY_LIMIT }),
withAuth(async (req, res, userId) => {
const { sessionId } = req.params;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,11 @@ import express, { Router } from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';

import {
iaChatAccessPermission,
iaChatManagePermission,
iaChatUsePermission,
iaMcpManagePermission,
iaMcpUsePermission,
iaNotebooksUsePermission,
iaChatPermission,
iaMcpToolsPermission,
iaNotebooksPermission,
iaPermissions,
iaSkillsAccessPermission,
iaSkillsPermission,
} from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';

import { Readable } from 'node:stream';
Expand Down Expand Up @@ -318,7 +315,7 @@ export async function createRouter(
router.get(
'/mcp-servers',
generalRateLimiter,
requirePermission(iaMcpUsePermission),
requirePermission(iaMcpToolsPermission),
async (req, res) => {
try {
const { userEntityRef } = getIdentity(req);
Expand Down Expand Up @@ -359,7 +356,7 @@ export async function createRouter(
router.post(
'/mcp-servers/validate',
generalRateLimiter,
requirePermission(iaMcpUsePermission),
requirePermission(iaMcpToolsPermission),
async (req, res) => {
try {
const { url, token } = req.body;
Expand Down Expand Up @@ -394,7 +391,7 @@ export async function createRouter(
router.post(
'/mcp-servers/:name/validate',
generalRateLimiter,
requirePermission(iaMcpManagePermission),
requirePermission(iaMcpToolsPermission),
async (req, res) => {
try {
const { userEntityRef, credentials } = getIdentity(req);
Expand Down Expand Up @@ -479,7 +476,7 @@ export async function createRouter(
router.patch(
'/mcp-servers/:name',
generalRateLimiter,
requirePermission(iaMcpManagePermission),
requirePermission(iaMcpToolsPermission),
async (req, res) => {
try {
const { userEntityRef } = getIdentity(req);
Expand Down Expand Up @@ -566,7 +563,7 @@ export async function createRouter(
router.get(
'/notebook-conversation-ids',
generalRateLimiter,
requirePermission(iaNotebooksUsePermission),
requirePermission(iaNotebooksPermission),
async (req, res) => {
try {
const { userEntityRef } = getIdentity(req);
Expand Down Expand Up @@ -608,70 +605,70 @@ export async function createRouter(
router.get(
'/v1/models',
generalRateLimiter,
requirePermission(iaChatAccessPermission),
requirePermission(iaChatPermission),
apiProxy,
);
router.get(
'/v1/shields',
generalRateLimiter,
requirePermission(iaChatAccessPermission),
requirePermission(iaChatPermission),
apiProxy,
);
router.get(
'/v2/conversations',
generalRateLimiter,
requirePermission(iaChatAccessPermission),
requirePermission(iaChatPermission),
apiProxy,
);
router.get(
'/v2/conversations/:conversation_id',
generalRateLimiter,
requirePermission(iaChatAccessPermission),
requirePermission(iaChatPermission),
apiProxy,
);
router.delete(
'/v2/conversations/:conversation_id',
generalRateLimiter,
requirePermission(iaChatManagePermission),
requirePermission(iaChatPermission),
apiProxy,
);
router.get(
'/v1/feedback/status',
generalRateLimiter,
requirePermission(iaChatAccessPermission),
requirePermission(iaChatPermission),
apiProxy,
);

router.get(
'/v1/saved-prompts/config',
generalRateLimiter,
requirePermission(iaChatUsePermission),
requirePermission(iaChatPermission),
apiProxy, // SKIP_USER_ID_ENDPOINTS prevents user_id injection for this endpoint
);
router.get(
'/v1/saved-prompts',
generalRateLimiter,
requirePermission(iaChatUsePermission),
requirePermission(iaChatPermission),
apiProxy,
);
router.delete(
'/v1/saved-prompts/:prompt_id',
generalRateLimiter,
requirePermission(iaChatUsePermission),
requirePermission(iaChatPermission),
apiProxy,
);

router.get(
'/v1/skills',
generalRateLimiter,
requirePermission(iaSkillsAccessPermission),
requirePermission(iaSkillsPermission),
apiProxy,
);

router.post(
'/v1/feedback',
generalRateLimiter,
requirePermission(iaChatUsePermission),
requirePermission(iaChatPermission),
async (request, response) => {
try {
const { userEntityRef } = getIdentity(request);
Expand Down Expand Up @@ -714,7 +711,7 @@ export async function createRouter(
router.post(
'/v1/saved-prompts',
generalRateLimiter,
requirePermission(iaChatUsePermission),
requirePermission(iaChatPermission),
async (request, response) => {
try {
const { userEntityRef } = getIdentity(request);
Expand Down Expand Up @@ -759,7 +756,7 @@ export async function createRouter(
router.post(
'/v1/query/interrupt',
generalRateLimiter,
requirePermission(iaChatUsePermission),
requirePermission(iaChatPermission),
async (request, response) => {
try {
const { userEntityRef } = getIdentity(request);
Expand Down Expand Up @@ -799,7 +796,7 @@ export async function createRouter(
expensiveRateLimiter,
validateCompletionsRequest,
validateAttachmentsForModel,
requirePermission(iaChatUsePermission),
requirePermission(iaChatPermission),
async (request, response) => {
const { provider }: Pick<QueryRequestBody, 'provider'> = request.body;
try {
Expand Down Expand Up @@ -892,7 +889,7 @@ export async function createRouter(
router.put(
'/v2/conversations/:conversation_id',
generalRateLimiter,
requirePermission(iaChatManagePermission),
requirePermission(iaChatPermission),
async (request, response) => {
try {
const { userEntityRef } = getIdentity(request);
Expand Down Expand Up @@ -933,7 +930,7 @@ export async function createRouter(
router.post(
'/v1/validate-model-vision',
generalRateLimiter,
requirePermission(iaChatUsePermission),
requirePermission(iaChatPermission),
async (request, response) => {
const { model, provider } = request.body;

Expand Down
Loading
Loading