mirror of
https://github.com/laurent22/joplin.git
synced 2025-03-26 21:12:59 +02:00
Server: Added API end points to manage users
This commit is contained in:
parent
daaaa133ab
commit
77b284f01f
Binary file not shown.
@ -277,7 +277,6 @@ export interface User extends WithDates, WithUuid {
|
||||
is_admin?: number;
|
||||
max_item_size?: number;
|
||||
can_share?: number;
|
||||
max_share_recipients?: number;
|
||||
}
|
||||
|
||||
export interface Session extends WithDates, WithUuid {
|
||||
@ -382,7 +381,6 @@ export const databaseSchema: DatabaseTables = {
|
||||
created_time: { type: 'string' },
|
||||
max_item_size: { type: 'number' },
|
||||
can_share: { type: 'number' },
|
||||
max_share_recipients: { type: 'number' },
|
||||
},
|
||||
sessions: {
|
||||
id: { type: 'string' },
|
||||
|
80
packages/server/src/routes/api/users.test.ts
Normal file
80
packages/server/src/routes/api/users.test.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { User } from '../../db';
|
||||
import { deleteApi, getApi, patchApi, postApi } from '../../utils/testing/apiUtils';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, createUserAndSession, models } from '../../utils/testing/testUtils';
|
||||
|
||||
describe('api_users', function() {
|
||||
|
||||
beforeAll(async () => {
|
||||
await beforeAllDb('api_users');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await afterAllTests();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await beforeEachDb();
|
||||
});
|
||||
|
||||
test('should create a user', async function() {
|
||||
const { session: adminSession } = await createUserAndSession(1, true);
|
||||
|
||||
const userToSave: User = {
|
||||
full_name: 'Toto',
|
||||
email: 'toto@example.com',
|
||||
max_item_size: 1000,
|
||||
can_share: 0,
|
||||
};
|
||||
|
||||
await postApi(adminSession.id, 'users', userToSave);
|
||||
|
||||
const savedUser = await models().user().loadByEmail('toto@example.com');
|
||||
expect(savedUser.full_name).toBe('Toto');
|
||||
expect(savedUser.email).toBe('toto@example.com');
|
||||
expect(savedUser.can_share).toBe(0);
|
||||
expect(savedUser.max_item_size).toBe(1000);
|
||||
});
|
||||
|
||||
test('should patch a user', async function() {
|
||||
const { session: adminSession } = await createUserAndSession(1, true);
|
||||
const { user } = await createUserAndSession(2);
|
||||
|
||||
await patchApi(adminSession.id, `users/${user.id}`, {
|
||||
max_item_size: 1000,
|
||||
});
|
||||
|
||||
const savedUser = await models().user().load(user.id);
|
||||
expect(savedUser.max_item_size).toBe(1000);
|
||||
});
|
||||
|
||||
test('should get a user', async function() {
|
||||
const { session: adminSession } = await createUserAndSession(1, true);
|
||||
const { user } = await createUserAndSession(2);
|
||||
|
||||
const fetchedUser: User = await getApi(adminSession.id, `users/${user.id}`);
|
||||
|
||||
expect(fetchedUser.id).toBe(user.id);
|
||||
expect(fetchedUser.email).toBe(user.email);
|
||||
});
|
||||
|
||||
test('should delete a user', async function() {
|
||||
const { session: adminSession } = await createUserAndSession(1, true);
|
||||
const { user } = await createUserAndSession(2);
|
||||
|
||||
await deleteApi(adminSession.id, `users/${user.id}`);
|
||||
|
||||
const loadedUser = await models().user().load(user.id);
|
||||
expect(loadedUser).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should list users', async function() {
|
||||
const { session: adminSession } = await createUserAndSession(1, true);
|
||||
await createUserAndSession(2);
|
||||
await createUserAndSession(3);
|
||||
|
||||
const results: any = await getApi(adminSession.id, 'users');
|
||||
console.info(results);
|
||||
expect(results.items.length).toBe(3);
|
||||
});
|
||||
|
||||
});
|
61
packages/server/src/routes/api/users.ts
Normal file
61
packages/server/src/routes/api/users.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { User } from '../../db';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { ErrorNotFound } from '../../utils/errors';
|
||||
import { AclAction } from '../../models/BaseModel';
|
||||
import uuidgen from '../../utils/uuidgen';
|
||||
|
||||
const router = new Router();
|
||||
|
||||
async function fetchUser(path: SubPath, ctx: AppContext): Promise<User> {
|
||||
const user = await ctx.models.user().load(path.id);
|
||||
if (!user) throw new ErrorNotFound(`No user with ID ${path.id}`);
|
||||
return user;
|
||||
}
|
||||
|
||||
async function postedUserFromContext(ctx: AppContext): Promise<User> {
|
||||
return ctx.models.user().fromApiInput(await bodyFields<any>(ctx.req));
|
||||
}
|
||||
|
||||
router.get('api/users/:id', async (path: SubPath, ctx: AppContext) => {
|
||||
const user = await fetchUser(path, ctx);
|
||||
await ctx.models.user().checkIfAllowed(ctx.owner, AclAction.Read, user);
|
||||
return user;
|
||||
});
|
||||
|
||||
router.post('api/users', async (_path: SubPath, ctx: AppContext) => {
|
||||
await ctx.models.user().checkIfAllowed(ctx.owner, AclAction.Create);
|
||||
const user = await postedUserFromContext(ctx);
|
||||
|
||||
// We set a random password because it's required, but user will have to
|
||||
// set it by clicking on the confirmation link.
|
||||
user.password = uuidgen();
|
||||
const output = await ctx.models.user().save(user);
|
||||
return ctx.models.user().toApiOutput(output);
|
||||
});
|
||||
|
||||
router.get('api/users', async (_path: SubPath, ctx: AppContext) => {
|
||||
await ctx.models.user().checkIfAllowed(ctx.owner, AclAction.List);
|
||||
|
||||
return {
|
||||
items: await ctx.models.user().all(),
|
||||
has_more: false,
|
||||
};
|
||||
});
|
||||
|
||||
router.del('api/users/:id', async (path: SubPath, ctx: AppContext) => {
|
||||
const user = await fetchUser(path, ctx);
|
||||
await ctx.models.user().checkIfAllowed(ctx.owner, AclAction.Delete, user);
|
||||
await ctx.models.user().delete(user.id);
|
||||
});
|
||||
|
||||
router.patch('api/users/:id', async (path: SubPath, ctx: AppContext) => {
|
||||
const user = await fetchUser(path, ctx);
|
||||
await ctx.models.user().checkIfAllowed(ctx.owner, AclAction.Update, user);
|
||||
const postedUser = await postedUserFromContext(ctx);
|
||||
await ctx.models.user().save({ id: user.id, ...postedUser });
|
||||
});
|
||||
|
||||
export default router;
|
@ -5,6 +5,7 @@ import apiEvents from './api/events';
|
||||
import apiItems from './api/items';
|
||||
import apiPing from './api/ping';
|
||||
import apiSessions from './api/sessions';
|
||||
import apiUsers from './api/users';
|
||||
import apiShares from './api/shares';
|
||||
import apiShareUsers from './api/share_users';
|
||||
|
||||
@ -27,6 +28,7 @@ const routes: Routers = {
|
||||
'api/sessions': apiSessions,
|
||||
'api/share_users': apiShareUsers,
|
||||
'api/shares': apiShares,
|
||||
'api/users': apiUsers,
|
||||
|
||||
'changes': indexChanges,
|
||||
'home': indexHome,
|
||||
|
@ -242,7 +242,7 @@ export const createUserAndSession = async function(index: number = 1, isAdmin: b
|
||||
const session = await models().session().authenticate(options.email, options.password);
|
||||
|
||||
return {
|
||||
user: user,
|
||||
user: await models().user().load(user.id),
|
||||
session: session,
|
||||
};
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user