Skip to content

Commit 083af97

Browse files
committed
feat: request additional user info
1 parent 2f3d88a commit 083af97

File tree

4 files changed

+55
-9
lines changed

4 files changed

+55
-9
lines changed

src/roles/util.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,18 @@ export class Map {
155155
id: user.userId,
156156
roleNames: user.roles,
157157
active: user.active,
158+
createdAt: Map.unknownDate(user.createdAt),
159+
lastUsedAt: Map.unknownDate(user.lastUsedAt),
158160
});
159161
static dbUsers = (users: WeaviateDBUser[]): UserDB[] => users.map(Map.dbUser);
160162
static assignedUsers = (users: WeaviateAssignedUser[]): UserAssignment[] =>
161163
users.map((user) => ({
162164
id: user.userId || '',
163165
userType: user.userType,
164166
}));
167+
static unknownDate = (date?: unknown): Date | undefined => (
168+
date !== undefined && typeof date === "string"
169+
? new Date(date) : undefined);
165170
}
166171

167172
class PermissionsMapping {

src/users/index.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from '../openapi/types.js';
99
import { Role } from '../roles/types.js';
1010
import { Map } from '../roles/util.js';
11-
import { AssignRevokeOptions, DeactivateOptions, GetAssignedRolesOptions, User, UserDB } from './types.js';
11+
import { AssignRevokeOptions, DeactivateOptions, GetAssignedRolesOptions, GetUserOptions, User, UserDB } from './types.js';
1212

1313
/**
1414
* Operations supported for 'db', 'oidc', and legacy (non-namespaced) users.
@@ -116,14 +116,14 @@ export interface DBUsers extends UsersBase {
116116
* @param {string} userId The ID of the user to get.
117117
* @returns {Promise<UserDB>} ID, status, and assigned roles of a 'db_*' user.
118118
*/
119-
byName: (userId: string) => Promise<UserDB>;
119+
byName: (userId: string, opts?: GetUserOptions) => Promise<UserDB>;
120120

121121
/**
122122
* List all 'db_user' / 'db_env_user' users.
123123
*
124124
* @returns {Promise<UserDB[]>} ID, status, and assigned roles for each 'db_*' user.
125125
*/
126-
listAll: () => Promise<UserDB[]>;
126+
listAll: (opts?: GetUserOptions) => Promise<UserDB[]>;
127127
}
128128

129129
/** Operations supported for namespaced 'oidc' users.*/
@@ -195,8 +195,8 @@ const db = (connection: ConnectionREST): DBUsers => {
195195
.postEmpty<DeactivateOptions | null>(`/users/db/${userId}/deactivate`, opts || null)
196196
.then(() => true)
197197
.catch(expectCode(409)),
198-
byName: (userId: string) => connection.get<WeaviateDBUser>(`/users/db/${userId}`, true).then(Map.dbUser),
199-
listAll: () => connection.get<WeaviateDBUser[]>('/users/db', true).then(Map.dbUsers),
198+
byName: (userId: string, opts?: GetUserOptions) => connection.get<WeaviateDBUser>(`/users/db/${userId}?includeLastUsedTime=${opts?.includeLastUsedTime || false}`, true).then(Map.dbUser),
199+
listAll: (opts?: GetUserOptions) => connection.get<WeaviateDBUser[]>(`/users/db?includeLastUsedTime=${opts?.includeLastUsedTime || false}`, true).then(Map.dbUsers),
200200
};
201201
};
202202

@@ -238,9 +238,7 @@ const namespacedUsers = (connection: ConnectionREST): NamespacedUsers => {
238238
getAssignedRoles: (userType: UserTypeInternal, userId: string, opts?: GetAssignedRolesOptions) =>
239239
connection
240240
.get<WeaviateRole[]>(
241-
`/authz/users/${userId}/roles/${userType}${
242-
opts?.includePermissions ? '?&includeFullRoles=true' : ''
243-
}`
241+
`/authz/users/${userId}/roles/${userType}?includeFullRoles=${opts?.includePermissions || false}`
244242
)
245243
.then(Map.roles),
246244
assignRoles: (roleNames: string | string[], userId: string, opts?: AssignRevokeOptions) =>

src/users/integration.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,46 @@ requireAtLeast(
160160
expect(roles.test.nodesPermissions).toHaveLength(1);
161161
});
162162

163+
requireAtLeast(1, 30, 1)('additional DUM features', () => {
164+
it('should be able to fetch additional user info', async () => {
165+
const admin = await makeClient('admin-key');
166+
const timKey = await admin.users.db.create('timely-tim');
167+
168+
// Get user info with / without lastUserTime
169+
let timUser = await admin.users.db.byName('timely-tim');
170+
expect(timUser.createdAt).not.toBeUndefined(); // always returned
171+
expect(timUser.lastUsedAt).toBeUndefined();
172+
173+
await expect(admin.users.db.byName('timely-tim', { includeLastUsedTime: true }))
174+
.resolves.toEqual(expect.any(Date));
175+
176+
// apiKeyFirstLetters contain first letters of the API key
177+
expect(timUser.apiKeyFirstLetters).not.toBeUndefined();
178+
expect(timKey).toMatch(new RegExp(`^${timUser.apiKeyFirstLetters}.*`))
179+
180+
// Check that Tim cannnot see the first letters of the API key
181+
const tim = await makeClient(timKey);
182+
expect(await tim.users.getMyUser()).resolves.toHaveProperty('apiKeyFirstLetters', undefined);
183+
});
184+
185+
it('should be able to list all users with additional info', async () => {
186+
const admin = await makeClient('admin-key');
187+
await admin.users.db.listAll({ includeLastUsedTime: true }).then(res => {
188+
res.forEach((user, _) => {
189+
expect(user).toEqual(expect.objectContaining({
190+
createdAt: expect.any(Date),
191+
lastUsedAt: expect.any(Date),
192+
apiKeyFirstLetters: expect.any(String),
193+
}));
194+
});
195+
});
196+
});
197+
})
198+
163199
afterAll(() =>
164200
makeClient('admin-key').then(async (c) => {
165201
await Promise.all(
166-
['jim', 'pam', 'dwight', 'dynamic-dave', 'api-ashley', 'role-rick', 'permission-peter'].map((n) =>
202+
['jim', 'pam', 'dwight', 'dynamic-dave', 'api-ashley', 'role-rick', 'permission-peter', 'timely-tim'].map((n) =>
167203
c.users.db.delete(n)
168204
)
169205
);

src/users/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ export type UserDB = {
1111
id: string;
1212
roleNames: string[];
1313
active: boolean;
14+
15+
createdAt?: Date;
16+
lastUsedAt?: Date;
17+
apiKeyFirstLetters?: string;
1418
};
1519

1620
/** Optional arguments to /user/{type}/{username} enpoint. */
@@ -23,3 +27,6 @@ export type AssignRevokeOptions = { userType?: WeaviateUserTypeInternal };
2327

2428
/** Optional arguments to /deactivate endpoint. */
2529
export type DeactivateOptions = { revokeKey?: boolean };
30+
31+
/** Optional arguments to /users and /users/<id> endpoints. */
32+
export type GetUserOptions = { includeLastUsedTime?: boolean };

0 commit comments

Comments
 (0)