mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
fix(console): fix m2m app log not accessible bug (#5307)
This commit is contained in:
parent
a4a02e2c66
commit
c2e6a610bc
5 changed files with 90 additions and 6 deletions
|
@ -98,6 +98,10 @@ function ConsoleContent() {
|
|||
<Route index element={<Navigate replace to={ApplicationDetailsTabs.Settings} />} />
|
||||
<Route path=":tab" element={<ApplicationDetails />} />
|
||||
</Route>
|
||||
<Route
|
||||
path={`:appId/${ApplicationDetailsTabs.Logs}/:logId`}
|
||||
element={<AuditLogDetails />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="api-resources">
|
||||
<Route index element={<ApiResources />} />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { withAppInsights } from '@logto/app-insights/react';
|
||||
import type { User, Log, Hook } from '@logto/schemas';
|
||||
import type { Application, User, Log, Hook } from '@logto/schemas';
|
||||
import { demoAppApplicationId } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -33,10 +33,11 @@ const isWebhookEventLog = (key?: string) =>
|
|||
key && Object.values<string>(hookEventLogKey).includes(key);
|
||||
|
||||
function AuditLogDetails() {
|
||||
const { userId, hookId, logId } = useParams();
|
||||
const { appId, userId, hookId, logId } = useParams();
|
||||
const { pathname } = useLocation();
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { data, error, mutate } = useSWR<Log, RequestError>(logId && `api/logs/${logId}`);
|
||||
const { data: appData } = useSWR<Application, RequestError>(appId && `api/applications/${appId}`);
|
||||
const { data: userData } = useSWR<User, RequestError>(userId && `api/users/${userId}`);
|
||||
const { data: hookData } = useSWR<Hook, RequestError>(hookId && `api/hooks/${hookId}`);
|
||||
|
||||
|
@ -54,7 +55,14 @@ function AuditLogDetails() {
|
|||
hookId &&
|
||||
t('log_details.back_to', {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
name: hookData?.name || t('users.unnamed'),
|
||||
name: hookData?.name || t('general.unnamed'),
|
||||
})
|
||||
) ??
|
||||
conditional(
|
||||
appId &&
|
||||
t('log_details.back_to', {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
name: appData?.name || t('general.unnamed'),
|
||||
})
|
||||
) ??
|
||||
t('log_details.back_to_logs');
|
||||
|
|
|
@ -29,3 +29,7 @@ export const authedAdminTenantApi = adminTenantApi.extend({
|
|||
export const cloudApi = got.extend({
|
||||
prefixUrl: new URL('/api', logtoCloudUrl),
|
||||
});
|
||||
|
||||
export const oidcApi = got.extend({
|
||||
prefixUrl: new URL('/oidc', logtoUrl),
|
||||
});
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import {
|
||||
ApplicationType,
|
||||
type Application,
|
||||
type CreateApplication,
|
||||
type ApplicationType,
|
||||
type OidcClientMetadata,
|
||||
type Role,
|
||||
type ProtectedAppMetadata,
|
||||
} from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
|
||||
import { authedAdminApi } from './api.js';
|
||||
import { authedAdminApi, oidcApi } from './api.js';
|
||||
|
||||
export const createApplication = async (
|
||||
name: string,
|
||||
|
@ -89,3 +89,20 @@ export const putRolesToApplication = async (applicationId: string, roleIds: stri
|
|||
|
||||
export const deleteRoleFromApplication = async (applicationId: string, roleId: string) =>
|
||||
authedAdminApi.delete(`applications/${applicationId}/roles/${roleId}`);
|
||||
|
||||
export const generateM2mLog = async (applicationId: string) => {
|
||||
const { id, secret, type, isThirdParty } = await getApplication(applicationId);
|
||||
|
||||
if (type !== ApplicationType.MachineToMachine || isThirdParty) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a token request with insufficient parameters and should fail. We make the request to generate a log for the current machine to machine app.
|
||||
return oidcApi.post('token', {
|
||||
form: {
|
||||
client_id: id,
|
||||
client_secret: secret,
|
||||
grant_type: 'client_credentials',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ApplicationType } from '@logto/schemas';
|
||||
|
||||
import { generateM2mLog } from '#src/api/application.js';
|
||||
import { logtoConsoleUrl as logtoConsoleUrlString } from '#src/constants.js';
|
||||
import {
|
||||
expectConfirmModalAndAct,
|
||||
|
@ -12,7 +13,7 @@ import {
|
|||
goToAdminConsole,
|
||||
waitForToast,
|
||||
} from '#src/ui-helpers/index.js';
|
||||
import { expectNavigation, appendPathname } from '#src/utils.js';
|
||||
import { expectNavigation, appendPathname, dcls } from '#src/utils.js';
|
||||
|
||||
import {
|
||||
type ApplicationMetadata,
|
||||
|
@ -255,6 +256,56 @@ describe('applications', () => {
|
|||
text: app.name,
|
||||
});
|
||||
|
||||
// Make sure the machine log details page can be accessed.
|
||||
// Machine logs only available for m2m apps.
|
||||
if (app.type === ApplicationType.MachineToMachine) {
|
||||
// Get the app id from the page.
|
||||
const appId = await page.$eval(
|
||||
[dcls('main'), dcls('header'), dcls('row'), dcls('copyId'), dcls('content')].join(' '),
|
||||
(element) => element.textContent
|
||||
);
|
||||
expect(appId).toBeTruthy();
|
||||
|
||||
await Promise.all([
|
||||
expect(generateM2mLog(appId!)).rejects.toThrow(),
|
||||
expect(page).toClick('nav div[class$=item] div[class$=link] a', {
|
||||
text: 'Machine logs',
|
||||
}),
|
||||
]);
|
||||
expect(page.url().endsWith('logs')).toBeTruthy();
|
||||
|
||||
// Logs were preloaded, so we need to reload the page to get the latest list of logs.
|
||||
await page.reload({ waitUntil: 'networkidle0' });
|
||||
|
||||
// Go to the details page of the log.
|
||||
await expect(page).toClick(
|
||||
'table tbody tr td div[class*=eventName]:has(div[class*=title])',
|
||||
{
|
||||
text: 'Exchange token by Client Credentials',
|
||||
}
|
||||
);
|
||||
|
||||
await expect(page).toMatchElement([dcls('main'), dcls('header'), dcls('label')].join(' '), {
|
||||
text: 'Failed',
|
||||
});
|
||||
await expect(page).toMatchElement(
|
||||
[dcls('main'), dcls('header'), dcls('content'), dcls('basicInfo'), 'a[class*=link]'].join(
|
||||
' '
|
||||
),
|
||||
{
|
||||
text: app.name,
|
||||
}
|
||||
);
|
||||
|
||||
// Go back to machine logs tab of the m2m app details page.
|
||||
await expect(page).toClick(
|
||||
[dcls('main'), dcls('container'), 'a[class*=backLink]', 'span'].join(' '),
|
||||
{
|
||||
text: `Back to ${app.name}`,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await expectToProceedAppDeletion(page, app.name);
|
||||
|
||||
expect(page.url()).toBe(new URL('/console/applications', logtoConsoleUrl).href);
|
||||
|
|
Loading…
Reference in a new issue