mirror of
https://github.com/directus/directus.git
synced 2026-04-03 03:00:39 -04:00
Add origin to accountability (#15273)
* Add origin to accountability * Remove origin column from seeds
This commit is contained in:
@@ -373,6 +373,7 @@ export function createLDAPAuthRouter(provider: string): Router {
|
||||
const accountability = {
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ export function createLocalAuthRouter(provider: string): Router {
|
||||
const accountability = {
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -276,6 +276,7 @@ export function createOAuth2AuthRouter(providerName: string): Router {
|
||||
accountability: {
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
role: null,
|
||||
},
|
||||
schema: req.schema,
|
||||
|
||||
@@ -300,6 +300,7 @@ export function createOpenIDAuthRouter(providerName: string): Router {
|
||||
accountability: {
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
role: null,
|
||||
},
|
||||
schema: req.schema,
|
||||
|
||||
@@ -92,6 +92,7 @@ router.post(
|
||||
user: req.accountability?.user,
|
||||
ip: getIPFromReq(req),
|
||||
user_agent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -59,6 +59,7 @@ router.post(
|
||||
const accountability = {
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
|
||||
@@ -101,6 +102,7 @@ router.post(
|
||||
const accountability = {
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
|
||||
@@ -141,6 +143,7 @@ router.post(
|
||||
const accountability = {
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
|
||||
@@ -175,6 +178,7 @@ router.post(
|
||||
const accountability = {
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable('directus_activity', (table) => {
|
||||
table.string('origin').nullable();
|
||||
});
|
||||
|
||||
await knex.schema.alterTable('directus_sessions', (table) => {
|
||||
table.string('origin').nullable();
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable('directus_activity', (table) => {
|
||||
table.dropColumn('origin');
|
||||
});
|
||||
|
||||
await knex.schema.alterTable('directus_sessions', (table) => {
|
||||
table.dropColumn('origin');
|
||||
});
|
||||
}
|
||||
@@ -60,6 +60,12 @@ fields:
|
||||
font: monospace
|
||||
width: half
|
||||
|
||||
- field: origin
|
||||
display: formatted-value
|
||||
display_options:
|
||||
font: monospace
|
||||
width: half
|
||||
|
||||
- field: ip
|
||||
display: formatted-value
|
||||
display_options:
|
||||
|
||||
@@ -11,4 +11,6 @@ fields:
|
||||
width: half
|
||||
- field: user_agent
|
||||
width: half
|
||||
- field: origin
|
||||
width: half
|
||||
- field: share
|
||||
|
||||
@@ -331,6 +331,7 @@ class FlowManager {
|
||||
collection: 'directus_flows',
|
||||
ip: accountability?.ip ?? null,
|
||||
user_agent: accountability?.userAgent ?? null,
|
||||
origin: accountability?.origin ?? null,
|
||||
item: flow.id,
|
||||
});
|
||||
|
||||
|
||||
@@ -39,7 +39,16 @@ test('Short-circuits when authenticate filter is used', async () => {
|
||||
test('Uses default public accountability when no token is given', async () => {
|
||||
const req = {
|
||||
ip: '127.0.0.1',
|
||||
get: jest.fn((string) => (string === 'user-agent' ? 'fake-user-agent' : null)),
|
||||
get: jest.fn((string) => {
|
||||
switch (string) {
|
||||
case 'user-agent':
|
||||
return 'fake-user-agent';
|
||||
case 'origin':
|
||||
return 'fake-origin';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
const res = {};
|
||||
@@ -56,6 +65,7 @@ test('Uses default public accountability when no token is given', async () => {
|
||||
app: false,
|
||||
ip: '127.0.0.1',
|
||||
userAgent: 'fake-user-agent',
|
||||
origin: 'fake-origin',
|
||||
});
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
@@ -87,7 +97,16 @@ test('Sets accountability to payload contents if valid token is passed', async (
|
||||
|
||||
const req = {
|
||||
ip: '127.0.0.1',
|
||||
get: jest.fn((string) => (string === 'user-agent' ? 'fake-user-agent' : null)),
|
||||
get: jest.fn((string) => {
|
||||
switch (string) {
|
||||
case 'user-agent':
|
||||
return 'fake-user-agent';
|
||||
case 'origin':
|
||||
return 'fake-origin';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
token,
|
||||
};
|
||||
|
||||
@@ -105,6 +124,7 @@ test('Sets accountability to payload contents if valid token is passed', async (
|
||||
share_scope: shareScope,
|
||||
ip: '127.0.0.1',
|
||||
userAgent: 'fake-user-agent',
|
||||
origin: 'fake-origin',
|
||||
});
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
@@ -136,6 +156,7 @@ test('Sets accountability to payload contents if valid token is passed', async (
|
||||
share_scope: shareScope,
|
||||
ip: '127.0.0.1',
|
||||
userAgent: 'fake-user-agent',
|
||||
origin: 'fake-origin',
|
||||
});
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
@@ -152,7 +173,16 @@ test('Throws InvalidCredentialsException when static token is used, but user doe
|
||||
|
||||
const req = {
|
||||
ip: '127.0.0.1',
|
||||
get: jest.fn((string) => (string === 'user-agent' ? 'fake-user-agent' : null)),
|
||||
get: jest.fn((string) => {
|
||||
switch (string) {
|
||||
case 'user-agent':
|
||||
return 'fake-user-agent';
|
||||
case 'origin':
|
||||
return 'fake-origin';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
token: 'static-token',
|
||||
};
|
||||
|
||||
@@ -166,7 +196,16 @@ test('Throws InvalidCredentialsException when static token is used, but user doe
|
||||
test('Sets accountability to user information when static token is used', async () => {
|
||||
const req = {
|
||||
ip: '127.0.0.1',
|
||||
get: jest.fn((string) => (string === 'user-agent' ? 'fake-user-agent' : null)),
|
||||
get: jest.fn((string) => {
|
||||
switch (string) {
|
||||
case 'user-agent':
|
||||
return 'fake-user-agent';
|
||||
case 'origin':
|
||||
return 'fake-origin';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
token: 'static-token',
|
||||
};
|
||||
|
||||
@@ -182,6 +221,7 @@ test('Sets accountability to user information when static token is used', async
|
||||
admin: testUser.admin_access,
|
||||
ip: '127.0.0.1',
|
||||
userAgent: 'fake-user-agent',
|
||||
origin: 'fake-origin',
|
||||
};
|
||||
|
||||
jest.mocked(getDatabase).mockReturnValue({
|
||||
|
||||
@@ -21,6 +21,7 @@ export const handler = async (req: Request, res: Response, next: NextFunction) =
|
||||
app: false,
|
||||
ip: getIPFromReq(req),
|
||||
userAgent: req.get('user-agent'),
|
||||
origin: req.get('origin'),
|
||||
};
|
||||
|
||||
const database = getDatabase();
|
||||
|
||||
@@ -216,6 +216,7 @@ export class AuthenticationService {
|
||||
expires: refreshTokenExpiration,
|
||||
ip: this.accountability?.ip,
|
||||
user_agent: this.accountability?.userAgent,
|
||||
origin: this.accountability?.origin,
|
||||
});
|
||||
|
||||
await this.knex('directus_sessions').delete().where('expires', '<', new Date());
|
||||
@@ -226,6 +227,7 @@ export class AuthenticationService {
|
||||
user: user.id,
|
||||
ip: this.accountability.ip,
|
||||
user_agent: this.accountability.userAgent,
|
||||
origin: this.accountability.origin,
|
||||
collection: 'directus_users',
|
||||
item: user.id,
|
||||
});
|
||||
|
||||
@@ -1955,6 +1955,7 @@ export class GraphQLService {
|
||||
const accountability = {
|
||||
ip: req?.ip,
|
||||
userAgent: req?.get('user-agent'),
|
||||
origin: req?.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
const authenticationService = new AuthenticationService({
|
||||
@@ -1988,6 +1989,7 @@ export class GraphQLService {
|
||||
const accountability = {
|
||||
ip: req?.ip,
|
||||
userAgent: req?.get('user-agent'),
|
||||
origin: req?.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
const authenticationService = new AuthenticationService({
|
||||
@@ -2024,6 +2026,7 @@ export class GraphQLService {
|
||||
const accountability = {
|
||||
ip: req?.ip,
|
||||
userAgent: req?.get('user-agent'),
|
||||
origin: req?.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
const authenticationService = new AuthenticationService({
|
||||
@@ -2048,6 +2051,7 @@ export class GraphQLService {
|
||||
const accountability = {
|
||||
ip: req?.ip,
|
||||
userAgent: req?.get('user-agent'),
|
||||
origin: req?.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
const service = new UsersService({ accountability, schema: this.schema });
|
||||
@@ -2073,6 +2077,7 @@ export class GraphQLService {
|
||||
const accountability = {
|
||||
ip: req?.ip,
|
||||
userAgent: req?.get('user-agent'),
|
||||
origin: req?.get('origin'),
|
||||
role: null,
|
||||
};
|
||||
const service = new UsersService({ accountability, schema: this.schema });
|
||||
@@ -2680,6 +2685,7 @@ export class GraphQLService {
|
||||
user: this.accountability?.user,
|
||||
ip: this.accountability?.ip,
|
||||
user_agent: this.accountability?.userAgent,
|
||||
origin: this.accountability?.origin,
|
||||
});
|
||||
|
||||
if ('directus_activity' in ReadCollectionTypes) {
|
||||
|
||||
@@ -185,6 +185,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
collection: this.collection,
|
||||
ip: this.accountability!.ip,
|
||||
user_agent: this.accountability!.userAgent,
|
||||
origin: this.accountability!.origin,
|
||||
item: primaryKey,
|
||||
});
|
||||
|
||||
@@ -549,6 +550,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
collection: this.collection,
|
||||
ip: this.accountability!.ip,
|
||||
user_agent: this.accountability!.userAgent,
|
||||
origin: this.accountability!.origin,
|
||||
item: key,
|
||||
}))
|
||||
);
|
||||
@@ -763,6 +765,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
collection: this.collection,
|
||||
ip: this.accountability!.ip,
|
||||
user_agent: this.accountability!.userAgent,
|
||||
origin: this.accountability!.origin,
|
||||
item: key,
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -101,6 +101,7 @@ export class SharesService extends ItemsService {
|
||||
expires: refreshTokenExpiration,
|
||||
ip: this.accountability?.ip,
|
||||
user_agent: this.accountability?.userAgent,
|
||||
origin: this.accountability?.origin,
|
||||
share: record.share_id,
|
||||
});
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ export function useRevisions(collection: Ref<string>, primaryKey: Ref<number | s
|
||||
'activity.user.last_name',
|
||||
'activity.ip',
|
||||
'activity.user_agent',
|
||||
'activity.origin',
|
||||
],
|
||||
meta: ['filter_count'],
|
||||
},
|
||||
@@ -115,6 +116,7 @@ export function useRevisions(collection: Ref<string>, primaryKey: Ref<number | s
|
||||
'activity.user.last_name',
|
||||
'activity.ip',
|
||||
'activity.user_agent',
|
||||
'activity.origin',
|
||||
],
|
||||
meta: ['filter_count'],
|
||||
},
|
||||
|
||||
@@ -543,6 +543,7 @@ activity_item: Activity Item
|
||||
action: Action
|
||||
ip_address: IP Address
|
||||
user_agent: User Agent
|
||||
origin: Origin
|
||||
webhooks: Webhooks
|
||||
decimals: Decimals
|
||||
value_decimals: Value Decimals
|
||||
@@ -1056,6 +1057,7 @@ fields:
|
||||
user: Action By
|
||||
comment: Comment
|
||||
user_agent: User Agent
|
||||
origin: Origin
|
||||
ip: IP Address
|
||||
revisions: Revisions
|
||||
directus_collections:
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
<p class="type-label">{{ t('user_agent') }}:</p>
|
||||
<p>{{ item.user_agent }}</p>
|
||||
|
||||
<p class="type-label">{{ t('origin') }}:</p>
|
||||
<p>{{ item.origin }}</p>
|
||||
|
||||
<p class="type-label">{{ t('collection') }}:</p>
|
||||
<p>{{ item.collection }}</p>
|
||||
|
||||
@@ -62,6 +65,7 @@ type ActivityRecord = {
|
||||
timestamp: string;
|
||||
ip: string;
|
||||
user_agent: string;
|
||||
origin: string;
|
||||
collection: string;
|
||||
item: string;
|
||||
};
|
||||
@@ -109,6 +113,7 @@ export default defineComponent({
|
||||
'timestamp',
|
||||
'ip',
|
||||
'user_agent',
|
||||
'origin',
|
||||
'collection',
|
||||
'item',
|
||||
],
|
||||
|
||||
@@ -8,6 +8,7 @@ export type Revision = {
|
||||
action: string;
|
||||
ip: string;
|
||||
user_agent: string;
|
||||
origin: string;
|
||||
timestamp: string;
|
||||
user:
|
||||
| string
|
||||
|
||||
@@ -16,4 +16,5 @@ export type Accountability = {
|
||||
|
||||
ip?: string;
|
||||
userAgent?: string;
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
@@ -37,6 +37,10 @@ properties:
|
||||
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/78.0.3904.108
|
||||
Safari/537.36
|
||||
type: string
|
||||
origin:
|
||||
description: Origin of the request when the action took place.
|
||||
example: https://directus.io
|
||||
type: string
|
||||
collection:
|
||||
description: Collection identifier in which the item resides.
|
||||
oneOf:
|
||||
|
||||
Reference in New Issue
Block a user