diff --git a/packages/backend/src/controllers/api/v1/admin/api-tokens/create-api-token.ee.js b/packages/backend/src/controllers/api/v1/admin/api-tokens/create-api-token.ee.js new file mode 100644 index 00000000..1a763c78 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/admin/api-tokens/create-api-token.ee.js @@ -0,0 +1,11 @@ +import { renderObject } from '../../../../../helpers/renderer.js'; +import ApiToken from '../../../../../models/api-token.ee.js'; + +export default async (request, response) => { + const apiToken = await ApiToken.query().insertAndFetch({}); + + renderObject(response, apiToken, { + serializer: 'AdminApiToken', + status: 201, + }); +}; diff --git a/packages/backend/src/controllers/api/v1/admin/api-tokens/create-api-token.ee.test.js b/packages/backend/src/controllers/api/v1/admin/api-tokens/create-api-token.ee.test.js new file mode 100644 index 00000000..7f2a735e --- /dev/null +++ b/packages/backend/src/controllers/api/v1/admin/api-tokens/create-api-token.ee.test.js @@ -0,0 +1,37 @@ +import { vi, describe, it, expect, beforeEach } from 'vitest'; +import request from 'supertest'; +import app from '../../../../../app.js'; +import ApiToken from '../../../../../models/api-token.ee.js'; +import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; +import { createRole } from '../../../../../../test/factories/role.js'; +import { createUser } from '../../../../../../test/factories/user.js'; +import createApiTokenMock from '../../../../../../test/mocks/rest/api/v1/admin/api-tokens/create-api-token.js'; +import * as license from '../../../../../helpers/license.ee.js'; + +describe('POST /api/v1/admin/api-tokens', () => { + let currentUser, token, role; + + beforeEach(async () => { + vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); + + role = await createRole({ name: 'Admin' }); + currentUser = await createUser({ roleId: role.id }); + + token = await createAuthTokenByUserId(currentUser.id); + }); + + it('should return the created api token', async () => { + const response = await request(app) + .post('/api/v1/admin/api-tokens') + .set('Authorization', token) + .expect(201); + + const refetchedToken = await ApiToken.query().findById( + response.body.data.id + ); + + const expectedPayload = await createApiTokenMock(refetchedToken); + + expect(response.body).toStrictEqual(expectedPayload); + }); +}); diff --git a/packages/backend/src/models/api-token.ee.js b/packages/backend/src/models/api-token.ee.js index aab7506f..9a38cdcb 100644 --- a/packages/backend/src/models/api-token.ee.js +++ b/packages/backend/src/models/api-token.ee.js @@ -9,6 +9,8 @@ class ApiToken extends Base { properties: { id: { type: 'string', format: 'uuid' }, token: { type: 'string', minLength: 32 }, + createdAt: { type: 'string' }, + updatedAt: { type: 'string' }, }, }; diff --git a/packages/backend/src/routes/api/v1/admin/api-tokens.ee.js b/packages/backend/src/routes/api/v1/admin/api-tokens.ee.js new file mode 100644 index 00000000..9c2e9276 --- /dev/null +++ b/packages/backend/src/routes/api/v1/admin/api-tokens.ee.js @@ -0,0 +1,17 @@ +import { Router } from 'express'; +import { authenticateUser } from '../../../../helpers/authentication.js'; +import { authorizeAdmin } from '../../../../helpers/authorization.js'; +import { checkIsEnterprise } from '../../../../helpers/check-is-enterprise.js'; +import createApiTokenAction from '../../../../controllers/api/v1/admin/api-tokens/create-api-token.ee.js'; + +const router = Router(); + +router.post( + '/', + authenticateUser, + authorizeAdmin, + checkIsEnterprise, + createApiTokenAction +); + +export default router; diff --git a/packages/backend/src/routes/index.js b/packages/backend/src/routes/index.js index 13634f77..5cfa3b6e 100644 --- a/packages/backend/src/routes/index.js +++ b/packages/backend/src/routes/index.js @@ -16,6 +16,7 @@ import adminAppsRouter from './api/v1/admin/apps.ee.js'; import adminConfigRouter from './api/v1/admin/config.ee.js'; import adminSamlAuthProvidersRouter from './api/v1/admin/saml-auth-providers.ee.js'; import adminTemplatesRouter from './api/v1/admin/templates.ee.js'; +import adminApiTokensRouter from './api/v1/admin/api-tokens.ee.js'; import templatesRouter from './api/v1/templates.ee.js'; import rolesRouter from './api/v1/admin/roles.ee.js'; import permissionsRouter from './api/v1/admin/permissions.ee.js'; @@ -45,6 +46,7 @@ router.use('/api/v1/admin/roles', rolesRouter); router.use('/api/v1/admin/permissions', permissionsRouter); router.use('/api/v1/admin/saml-auth-providers', adminSamlAuthProvidersRouter); router.use('/api/v1/admin/templates', adminTemplatesRouter); +router.use('/api/v1/admin/api-tokens', adminApiTokensRouter); router.use('/api/v1/templates', templatesRouter); router.use('/api/v1/installation/users', installationUsersRouter); router.use('/api/v1/folders', foldersRouter); diff --git a/packages/backend/src/serializers/index.js b/packages/backend/src/serializers/index.js index 1a28c798..96dbebeb 100644 --- a/packages/backend/src/serializers/index.js +++ b/packages/backend/src/serializers/index.js @@ -31,6 +31,7 @@ const serializers = { Permission: permissionSerializer, AdminSamlAuthProvider: adminSamlAuthProviderSerializer, AdminTemplate: adminTemplateSerializer, + AdminApiToken: adminApiTokenSerializer, Template: templateSerializer, SamlAuthProvider: samlAuthProviderSerializer, RoleMapping: samlAuthProviderRoleMappingSerializer, diff --git a/packages/backend/test/mocks/rest/api/v1/admin/api-tokens/create-api-token.js b/packages/backend/test/mocks/rest/api/v1/admin/api-tokens/create-api-token.js new file mode 100644 index 00000000..1bd0f310 --- /dev/null +++ b/packages/backend/test/mocks/rest/api/v1/admin/api-tokens/create-api-token.js @@ -0,0 +1,21 @@ +const createApiTokenMock = async (apiToken) => { + const data = { + id: apiToken.id, + token: apiToken.token, + createdAt: apiToken.createdAt.getTime(), + updatedAt: apiToken.updatedAt.getTime(), + }; + + return { + data: data, + meta: { + count: 1, + currentPage: null, + isArray: false, + totalPages: null, + type: 'ApiToken', + }, + }; +}; + +export default createApiTokenMock;