Merge pull request #26125 from backstage/rugvip/us-cleanup
user-settings-backend: remove usage of identity service
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-user-settings-backend': patch
|
||||
---
|
||||
|
||||
Replaced usage of the deprecated identity service with the new HTTP auth service for the new backend system.
|
||||
@@ -3,9 +3,9 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { DatabaseService } from '@backstage/backend-plugin-api';
|
||||
import express from 'express';
|
||||
import { IdentityApi } from '@backstage/plugin-auth-node';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { SignalsService } from '@backstage/plugin-signals-node';
|
||||
|
||||
// @public
|
||||
@@ -14,7 +14,7 @@ export function createRouter(options: RouterOptions): Promise<express.Router>;
|
||||
// @public (undocumented)
|
||||
export interface RouterOptions {
|
||||
// (undocumented)
|
||||
database: PluginDatabaseManager;
|
||||
database: DatabaseService;
|
||||
// (undocumented)
|
||||
identity: IdentityApi;
|
||||
// (undocumented)
|
||||
|
||||
@@ -18,8 +18,9 @@ import {
|
||||
coreServices,
|
||||
createBackendPlugin,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { createRouter } from './service/router';
|
||||
import { createRouterInternal } from './service/router';
|
||||
import { signalsServiceRef } from '@backstage/plugin-signals-node';
|
||||
import { DatabaseUserSettingsStore } from './database/DatabaseUserSettingsStore';
|
||||
|
||||
/**
|
||||
* The user settings backend plugin.
|
||||
@@ -32,12 +33,17 @@ export default createBackendPlugin({
|
||||
env.registerInit({
|
||||
deps: {
|
||||
database: coreServices.database,
|
||||
identity: coreServices.identity,
|
||||
httpAuth: coreServices.httpAuth,
|
||||
httpRouter: coreServices.httpRouter,
|
||||
signals: signalsServiceRef,
|
||||
},
|
||||
async init({ database, identity, httpRouter, signals }) {
|
||||
httpRouter.use(await createRouter({ database, identity, signals }));
|
||||
async init({ database, httpAuth, httpRouter, signals }) {
|
||||
const userSettingsStore = await DatabaseUserSettingsStore.create({
|
||||
database,
|
||||
});
|
||||
httpRouter.use(
|
||||
await createRouterInternal({ userSettingsStore, httpAuth, signals }),
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -14,15 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
BackstageIdentityResponse,
|
||||
IdentityApi,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import { UserSettingsStore } from '../database/UserSettingsStore';
|
||||
import { createRouterInternal } from './router';
|
||||
import { SignalsService } from '@backstage/plugin-signals-node';
|
||||
import { mockCredentials, mockServices } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('createRouter', () => {
|
||||
const userSettingsStore: jest.Mocked<UserSettingsStore> = {
|
||||
@@ -30,13 +27,6 @@ describe('createRouter', () => {
|
||||
set: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
const getIdentityMock = jest.fn<
|
||||
Promise<BackstageIdentityResponse | undefined>,
|
||||
any
|
||||
>();
|
||||
const identityApi: jest.Mocked<Partial<IdentityApi>> = {
|
||||
getIdentity: getIdentityMock,
|
||||
};
|
||||
const signalService: jest.Mocked<SignalsService> = {
|
||||
publish: jest.fn(),
|
||||
};
|
||||
@@ -46,7 +36,7 @@ describe('createRouter', () => {
|
||||
beforeEach(async () => {
|
||||
const router = await createRouterInternal({
|
||||
userSettingsStore,
|
||||
identity: identityApi as IdentityApi,
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
signals: signalService as SignalsService,
|
||||
});
|
||||
|
||||
@@ -57,40 +47,33 @@ describe('createRouter', () => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
const mockUserRef = mockCredentials.user().principal.userEntityRef;
|
||||
|
||||
describe('GET /buckets/:bucket/keys/:key', () => {
|
||||
it('returns ok', async () => {
|
||||
const setting = { bucket: 'my-bucket', key: 'my-key', value: 'a' };
|
||||
getIdentityMock.mockResolvedValue({
|
||||
token: 'a',
|
||||
identity: {
|
||||
type: 'user',
|
||||
ownershipEntityRefs: [],
|
||||
userEntityRef: 'user-1',
|
||||
},
|
||||
});
|
||||
|
||||
userSettingsStore.get.mockResolvedValue(setting);
|
||||
|
||||
const responses = await request(app)
|
||||
.get('/buckets/my-bucket/keys/my-key')
|
||||
.set('Authorization', 'Bearer foo');
|
||||
const responses = await request(app).get(
|
||||
'/buckets/my-bucket/keys/my-key',
|
||||
);
|
||||
|
||||
expect(responses.status).toEqual(200);
|
||||
expect(responses.body).toEqual(setting);
|
||||
|
||||
expect(getIdentityMock).toHaveBeenCalledTimes(1);
|
||||
expect(userSettingsStore.get).toHaveBeenCalledTimes(1);
|
||||
expect(userSettingsStore.get).toHaveBeenCalledWith({
|
||||
userEntityRef: 'user-1',
|
||||
userEntityRef: mockUserRef,
|
||||
bucket: 'my-bucket',
|
||||
key: 'my-key',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the Authorization header is missing', async () => {
|
||||
const responses = await request(app).get(
|
||||
'/buckets/my-bucket/keys/my-key',
|
||||
);
|
||||
const responses = await request(app)
|
||||
.get('/buckets/my-bucket/keys/my-key')
|
||||
.set('Authorization', mockCredentials.none.header());
|
||||
|
||||
expect(responses.status).toEqual(401);
|
||||
expect(userSettingsStore.get).not.toHaveBeenCalled();
|
||||
@@ -99,41 +82,31 @@ describe('createRouter', () => {
|
||||
|
||||
describe('DELETE /buckets/:bucket/keys/:key', () => {
|
||||
it('returns ok', async () => {
|
||||
getIdentityMock.mockResolvedValue({
|
||||
token: 'a',
|
||||
identity: {
|
||||
type: 'user',
|
||||
ownershipEntityRefs: [],
|
||||
userEntityRef: 'user-1',
|
||||
},
|
||||
});
|
||||
|
||||
userSettingsStore.delete.mockResolvedValue();
|
||||
|
||||
const responses = await request(app)
|
||||
.delete('/buckets/my-bucket/keys/my-key')
|
||||
.set('Authorization', 'Bearer foo');
|
||||
const responses = await request(app).delete(
|
||||
'/buckets/my-bucket/keys/my-key',
|
||||
);
|
||||
|
||||
expect(responses.status).toEqual(204);
|
||||
|
||||
expect(getIdentityMock).toHaveBeenCalledTimes(1);
|
||||
expect(userSettingsStore.delete).toHaveBeenCalledTimes(1);
|
||||
expect(userSettingsStore.delete).toHaveBeenCalledWith({
|
||||
userEntityRef: 'user-1',
|
||||
userEntityRef: mockUserRef,
|
||||
bucket: 'my-bucket',
|
||||
key: 'my-key',
|
||||
});
|
||||
expect(signalService.publish).toHaveBeenCalledWith({
|
||||
recipients: { type: 'user', entityRef: 'user-1' },
|
||||
recipients: { type: 'user', entityRef: mockUserRef },
|
||||
channel: `user-settings`,
|
||||
message: { type: 'key-deleted', key: 'my-key' },
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the Authorization header is missing', async () => {
|
||||
const responses = await request(app).delete(
|
||||
'/buckets/my-bucket/keys/my-key',
|
||||
);
|
||||
const responses = await request(app)
|
||||
.delete('/buckets/my-bucket/keys/my-key')
|
||||
.set('Authorization', mockCredentials.none.header());
|
||||
|
||||
expect(responses.status).toEqual(401);
|
||||
expect(userSettingsStore.delete).not.toHaveBeenCalled();
|
||||
@@ -143,72 +116,51 @@ describe('createRouter', () => {
|
||||
describe('PUT /buckets/:bucket/keys/:key', () => {
|
||||
it('returns ok', async () => {
|
||||
const setting = { bucket: 'my-bucket', key: 'my-key', value: 'a' };
|
||||
getIdentityMock.mockResolvedValue({
|
||||
token: 'a',
|
||||
identity: {
|
||||
type: 'user',
|
||||
ownershipEntityRefs: [],
|
||||
userEntityRef: 'user-1',
|
||||
},
|
||||
});
|
||||
|
||||
userSettingsStore.set.mockResolvedValue();
|
||||
userSettingsStore.get.mockResolvedValue(setting);
|
||||
|
||||
const responses = await request(app)
|
||||
.put('/buckets/my-bucket/keys/my-key')
|
||||
.set('Authorization', 'Bearer foo')
|
||||
.send({ value: 'a' });
|
||||
|
||||
expect(responses.status).toEqual(200);
|
||||
expect(responses.body).toEqual(setting);
|
||||
|
||||
expect(getIdentityMock).toHaveBeenCalledTimes(1);
|
||||
expect(userSettingsStore.set).toHaveBeenCalledTimes(1);
|
||||
expect(userSettingsStore.set).toHaveBeenCalledWith({
|
||||
userEntityRef: 'user-1',
|
||||
userEntityRef: mockUserRef,
|
||||
bucket: 'my-bucket',
|
||||
key: 'my-key',
|
||||
value: 'a',
|
||||
});
|
||||
expect(userSettingsStore.get).toHaveBeenCalledTimes(1);
|
||||
expect(userSettingsStore.get).toHaveBeenCalledWith({
|
||||
userEntityRef: 'user-1',
|
||||
userEntityRef: mockUserRef,
|
||||
bucket: 'my-bucket',
|
||||
key: 'my-key',
|
||||
});
|
||||
expect(signalService.publish).toHaveBeenCalledWith({
|
||||
recipients: { type: 'user', entityRef: 'user-1' },
|
||||
recipients: { type: 'user', entityRef: mockUserRef },
|
||||
channel: `user-settings`,
|
||||
message: { type: 'key-changed', key: 'my-key' },
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the value is not given', async () => {
|
||||
getIdentityMock.mockResolvedValue({
|
||||
token: 'a',
|
||||
identity: {
|
||||
type: 'user',
|
||||
ownershipEntityRefs: [],
|
||||
userEntityRef: 'user-1',
|
||||
},
|
||||
});
|
||||
|
||||
const responses = await request(app)
|
||||
.put('/buckets/my-bucket/keys/my-key')
|
||||
.set('Authorization', 'Bearer foo')
|
||||
.send({});
|
||||
|
||||
expect(responses.status).toEqual(400);
|
||||
|
||||
expect(getIdentityMock).toHaveBeenCalledTimes(1);
|
||||
expect(userSettingsStore.set).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns an error if the Authorization header is missing', async () => {
|
||||
const responses = await request(app).get(
|
||||
'/buckets/my-bucket/keys/my-key',
|
||||
);
|
||||
const responses = await request(app)
|
||||
.get('/buckets/my-bucket/keys/my-key')
|
||||
.set('Authorization', mockCredentials.none.header());
|
||||
|
||||
expect(responses.status).toEqual(401);
|
||||
expect(userSettingsStore.get).not.toHaveBeenCalled();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { errorHandler, PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { errorHandler } from '@backstage/backend-common';
|
||||
import { AuthenticationError, InputError } from '@backstage/errors';
|
||||
import { IdentityApi } from '@backstage/plugin-auth-node';
|
||||
import express, { Request } from 'express';
|
||||
@@ -23,12 +23,16 @@ import { DatabaseUserSettingsStore } from '../database/DatabaseUserSettingsStore
|
||||
import { UserSettingsStore } from '../database/UserSettingsStore';
|
||||
import { SignalsService } from '@backstage/plugin-signals-node';
|
||||
import { UserSettingsSignal } from '@backstage/plugin-user-settings-common';
|
||||
import {
|
||||
DatabaseService,
|
||||
HttpAuthService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface RouterOptions {
|
||||
database: PluginDatabaseManager;
|
||||
database: DatabaseService;
|
||||
identity: IdentityApi;
|
||||
signals?: SignalsService;
|
||||
}
|
||||
@@ -52,11 +56,19 @@ export async function createRouter(
|
||||
});
|
||||
}
|
||||
|
||||
export async function createRouterInternal(options: {
|
||||
identity: IdentityApi;
|
||||
userSettingsStore: UserSettingsStore;
|
||||
signals?: SignalsService;
|
||||
}): Promise<express.Router> {
|
||||
export async function createRouterInternal(
|
||||
options:
|
||||
| {
|
||||
identity: IdentityApi;
|
||||
userSettingsStore: UserSettingsStore;
|
||||
signals?: SignalsService;
|
||||
}
|
||||
| {
|
||||
httpAuth: HttpAuthService;
|
||||
userSettingsStore: UserSettingsStore;
|
||||
signals?: SignalsService;
|
||||
},
|
||||
): Promise<express.Router> {
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
|
||||
@@ -64,6 +76,13 @@ export async function createRouterInternal(options: {
|
||||
* Helper method to extract the userEntityRef from the request.
|
||||
*/
|
||||
const getUserEntityRef = async (req: Request): Promise<string> => {
|
||||
if ('httpAuth' in options) {
|
||||
const credentials = await options.httpAuth.credentials(req, {
|
||||
allow: ['user'],
|
||||
});
|
||||
return credentials.principal.userEntityRef;
|
||||
}
|
||||
|
||||
// throws an AuthenticationError in case the token exists but is invalid
|
||||
const identity = await options.identity.getIdentity({ request: req });
|
||||
if (!identity) {
|
||||
|
||||
Reference in New Issue
Block a user