This repository has been archived on 2022-08-14. You can view files and clone it, but cannot push or open issues or pull requests.
outline/server/api/users.js

224 lines
5.0 KiB
JavaScript
Raw Normal View History

// @flow
import Router from "koa-router";
import userInviter from "../commands/userInviter";
import userSuspender from "../commands/userSuspender";
import auth from "../middlewares/authentication";
import { Event, User, Team } from "../models";
import policy from "../policies";
import { presentUser } from "../presenters";
import { Op } from "../sequelize";
import pagination from "./middlewares/pagination";
2016-04-29 05:25:37 +00:00
2018-03-04 23:38:51 +00:00
const { authorize } = policy;
2016-04-29 05:25:37 +00:00
const router = new Router();
router.post("users.list", auth(), pagination(), async (ctx) => {
const { sort = "createdAt", query, includeSuspended = false } = ctx.body;
let direction = ctx.body.direction;
if (direction !== "ASC") direction = "DESC";
const user = ctx.state.user;
feat: Memberships (#1032) * WIP * feat: Add collection.memberships endpoint * feat: Add ability to filter collection.memberships with query * WIP * Merge stashed work * feat: Add ability to filter memberships by permission * continued refactoring * paginated list component * Collection member management * fix: Incorrect policy data sent down after collection.update * Reduce duplication, add empty state * cleanup * fix: Modal close should be a real button * fix: Allow opening edit from modal * fix: remove unused methods * test: fix * Passing test suite * Refactor * fix: Flow UI errors * test: Add collections.update tests * lint * test: moar tests * fix: Missing scopes, more missing tests * fix: Handle collection privacy change over socket * fix: More membership scopes * fix: view endpoint permissions * fix: respond to privacy change on socket event * policy driven menus * fix: share endpoint policies * chore: Use policies to drive documents UI * alignment * fix: Header height * fix: Correct behavior when collection becomes private * fix: Header height for read-only collection * send id's over socket instead of serialized objects * fix: Remote policy change * fix: reduce collection fetching * More websocket efficiencies * fix: Document collection pinning * fix: Restored ability to edit drafts fix: Removed ability to star drafts * fix: Require write permissions to pin doc to collection * fix: Header title overlaying document actions at small screen sizes * fix: Jank on load caused by previous commit * fix: Double collection fetch post-publish * fix: Hide publish button if draft is in no longer accessible collection * fix: Always allow deleting drafts fix: Improved handling of deleted documents * feat: Show collections in drafts view feat: Show more obvious 'draft' badge on documents * fix: incorrect policies after publish to private collection * fix: Duplicating a draft publishes it
2019-10-06 01:42:03 +00:00
let where = {
teamId: user.teamId,
};
if (!includeSuspended) {
where = {
...where,
suspendedAt: {
[Op.eq]: null,
},
};
}
feat: Memberships (#1032) * WIP * feat: Add collection.memberships endpoint * feat: Add ability to filter collection.memberships with query * WIP * Merge stashed work * feat: Add ability to filter memberships by permission * continued refactoring * paginated list component * Collection member management * fix: Incorrect policy data sent down after collection.update * Reduce duplication, add empty state * cleanup * fix: Modal close should be a real button * fix: Allow opening edit from modal * fix: remove unused methods * test: fix * Passing test suite * Refactor * fix: Flow UI errors * test: Add collections.update tests * lint * test: moar tests * fix: Missing scopes, more missing tests * fix: Handle collection privacy change over socket * fix: More membership scopes * fix: view endpoint permissions * fix: respond to privacy change on socket event * policy driven menus * fix: share endpoint policies * chore: Use policies to drive documents UI * alignment * fix: Header height * fix: Correct behavior when collection becomes private * fix: Header height for read-only collection * send id's over socket instead of serialized objects * fix: Remote policy change * fix: reduce collection fetching * More websocket efficiencies * fix: Document collection pinning * fix: Restored ability to edit drafts fix: Removed ability to star drafts * fix: Require write permissions to pin doc to collection * fix: Header title overlaying document actions at small screen sizes * fix: Jank on load caused by previous commit * fix: Double collection fetch post-publish * fix: Hide publish button if draft is in no longer accessible collection * fix: Always allow deleting drafts fix: Improved handling of deleted documents * feat: Show collections in drafts view feat: Show more obvious 'draft' badge on documents * fix: incorrect policies after publish to private collection * fix: Duplicating a draft publishes it
2019-10-06 01:42:03 +00:00
if (query) {
where = {
...where,
name: {
[Op.iLike]: `%${query}%`,
},
};
}
const users = await User.findAll({
feat: Memberships (#1032) * WIP * feat: Add collection.memberships endpoint * feat: Add ability to filter collection.memberships with query * WIP * Merge stashed work * feat: Add ability to filter memberships by permission * continued refactoring * paginated list component * Collection member management * fix: Incorrect policy data sent down after collection.update * Reduce duplication, add empty state * cleanup * fix: Modal close should be a real button * fix: Allow opening edit from modal * fix: remove unused methods * test: fix * Passing test suite * Refactor * fix: Flow UI errors * test: Add collections.update tests * lint * test: moar tests * fix: Missing scopes, more missing tests * fix: Handle collection privacy change over socket * fix: More membership scopes * fix: view endpoint permissions * fix: respond to privacy change on socket event * policy driven menus * fix: share endpoint policies * chore: Use policies to drive documents UI * alignment * fix: Header height * fix: Correct behavior when collection becomes private * fix: Header height for read-only collection * send id's over socket instead of serialized objects * fix: Remote policy change * fix: reduce collection fetching * More websocket efficiencies * fix: Document collection pinning * fix: Restored ability to edit drafts fix: Removed ability to star drafts * fix: Require write permissions to pin doc to collection * fix: Header title overlaying document actions at small screen sizes * fix: Jank on load caused by previous commit * fix: Double collection fetch post-publish * fix: Hide publish button if draft is in no longer accessible collection * fix: Always allow deleting drafts fix: Improved handling of deleted documents * feat: Show collections in drafts view feat: Show more obvious 'draft' badge on documents * fix: incorrect policies after publish to private collection * fix: Duplicating a draft publishes it
2019-10-06 01:42:03 +00:00
where,
order: [[sort, direction]],
offset: ctx.state.pagination.offset,
limit: ctx.state.pagination.limit,
});
ctx.body = {
pagination: ctx.state.pagination,
data: users.map((listUser) =>
presentUser(listUser, { includeDetails: user.isAdmin })
),
};
});
router.post("users.info", auth(), async (ctx) => {
2019-04-19 01:51:16 +00:00
ctx.body = {
data: presentUser(ctx.state.user),
};
2016-05-20 06:51:22 +00:00
});
router.post("users.update", auth(), async (ctx) => {
const { user } = ctx.state;
feat: I18n (#1653) * feat: i18n * Changing language single source of truth from TEAM to USER * Changes according to @tommoor comments on PR * Changed package.json for build:i18n and translation label * Finished 1st MVP of i18n for outline * new translation labels & Portuguese from Portugal translation * Fixes from PR request * Described language dropdown as an experimental feature * Set keySeparator to false in order to cowork with html keys * Added useTranslation to Breadcrumb * Repositioned <strong> element * Removed extra space from TemplatesMenu * Fortified the test suite for i18n * Fixed trans component problematic * Check if selected language is available * Update yarn.lock * Removed unused Trans * Removing debug variable from i18n init * Removed debug variable * test: update snapshots * flow: Remove decorator usage to get proper flow typing It's a shame, but hopefully we'll move to Typescript in the next 6 months and we can forget this whole Flow mistake ever happened * translate: Drafts * More translatable strings * Mo translation strings * translation: Search * async translations loading * cache translations in client * Revert "cache translations in client" This reverts commit 08fb61ce36384ff90a704faffe4761eccfb76da1. * Revert localStorage cache for cache headers * Update Crowdin configuration file * Moved translation files to locales folder and fixed english text * Added CONTRIBUTING File for CrowdIn * chore: Move translations again to please CrowdIn * fix: loading paths chore: Add strings for editor * fix: Improve validation on documents.import endpoint * test: mock bull * fix: Unknown mimetype should fallback to Markdown parsing if markdown extension (#1678) * closes #1675 * Update CONTRIBUTING * chore: Add link to translation portal from app UI * refactor: Centralize language config * fix: Ensure creation of i18n directory in build * feat: Add language prompt * chore: Improve contributing guidelines, add link from README * chore: Normalize tab header casing * chore: More string externalization * fix: Language prompt in dark mode Co-authored-by: André Glatzl <andreglatzl@gmail.com>
2020-11-30 04:04:58 +00:00
const { name, avatarUrl, language } = ctx.body;
if (name) user.name = name;
if (avatarUrl) user.avatarUrl = avatarUrl;
feat: I18n (#1653) * feat: i18n * Changing language single source of truth from TEAM to USER * Changes according to @tommoor comments on PR * Changed package.json for build:i18n and translation label * Finished 1st MVP of i18n for outline * new translation labels & Portuguese from Portugal translation * Fixes from PR request * Described language dropdown as an experimental feature * Set keySeparator to false in order to cowork with html keys * Added useTranslation to Breadcrumb * Repositioned <strong> element * Removed extra space from TemplatesMenu * Fortified the test suite for i18n * Fixed trans component problematic * Check if selected language is available * Update yarn.lock * Removed unused Trans * Removing debug variable from i18n init * Removed debug variable * test: update snapshots * flow: Remove decorator usage to get proper flow typing It's a shame, but hopefully we'll move to Typescript in the next 6 months and we can forget this whole Flow mistake ever happened * translate: Drafts * More translatable strings * Mo translation strings * translation: Search * async translations loading * cache translations in client * Revert "cache translations in client" This reverts commit 08fb61ce36384ff90a704faffe4761eccfb76da1. * Revert localStorage cache for cache headers * Update Crowdin configuration file * Moved translation files to locales folder and fixed english text * Added CONTRIBUTING File for CrowdIn * chore: Move translations again to please CrowdIn * fix: loading paths chore: Add strings for editor * fix: Improve validation on documents.import endpoint * test: mock bull * fix: Unknown mimetype should fallback to Markdown parsing if markdown extension (#1678) * closes #1675 * Update CONTRIBUTING * chore: Add link to translation portal from app UI * refactor: Centralize language config * fix: Ensure creation of i18n directory in build * feat: Add language prompt * chore: Improve contributing guidelines, add link from README * chore: Normalize tab header casing * chore: More string externalization * fix: Language prompt in dark mode Co-authored-by: André Glatzl <andreglatzl@gmail.com>
2020-11-30 04:04:58 +00:00
if (language) user.language = language;
2018-05-31 19:44:32 +00:00
await user.save();
await Event.create({
name: "users.update",
actorId: user.id,
userId: user.id,
teamId: user.teamId,
ip: ctx.request.ip,
});
2019-04-19 01:51:16 +00:00
ctx.body = {
data: presentUser(user, { includeDetails: true }),
};
});
2018-03-04 23:38:51 +00:00
// Admin specific
router.post("users.promote", auth(), async (ctx) => {
2018-03-04 23:38:51 +00:00
const userId = ctx.body.id;
const teamId = ctx.state.user.teamId;
ctx.assertPresent(userId, "id is required");
2018-03-04 23:38:51 +00:00
const user = await User.findByPk(userId);
authorize(ctx.state.user, "promote", user);
2018-03-04 23:38:51 +00:00
const team = await Team.findByPk(teamId);
2018-03-04 23:38:51 +00:00
await team.addAdmin(user);
await Event.create({
name: "users.promote",
actorId: ctx.state.user.id,
userId,
teamId,
data: { name: user.name },
ip: ctx.request.ip,
});
2018-03-04 23:38:51 +00:00
ctx.body = {
data: presentUser(user, { includeDetails: true }),
2018-03-04 23:38:51 +00:00
};
});
router.post("users.demote", auth(), async (ctx) => {
2018-03-04 23:38:51 +00:00
const userId = ctx.body.id;
const teamId = ctx.state.user.teamId;
ctx.assertPresent(userId, "id is required");
2018-03-04 23:38:51 +00:00
const user = await User.findByPk(userId);
authorize(ctx.state.user, "demote", user);
2018-03-04 23:38:51 +00:00
const team = await Team.findByPk(teamId);
feat: Add groups and group permissions (#1204) * WIP - got one API test to pass yay * adds group update endpoint * added group policies * adds groups.list API * adds groups.info * remove comment * WIP * tests for delete * adds group membership list * adds tests for groups list * add and remove user endpoints for group * ask some questions * fix up some issues around primary keys * remove export from group permissions Co-Authored-By: Tom Moor <tom.moor@gmail.com> * remove random file * only create events on actual updates, add tests to ensure * adds uniqueness validation to group name * throw validation errors on model and let it pass through the controller * fix linting * WIP * WIP * WIP * WIP * WIP basic edit and delete * basic CRUD for groups and memberships in place * got member counts working * add member count and limit the number of users sent over teh wire to 6 * factor avatar with AvatarWithPresence into its own class * wip * WIP avatars in group lists * WIP collection groups * add and remove group endpoints * wip add collection groups * wip get group adding to collections to work * wip get updating collection group memberships to work * wip get new group modal working * add tests for collection index * include collection groups in the withmemberships scope * tie permissions to group memberships * remove unused import * Update app/components/GroupListItem.js update title copy Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/migrations/20191211044318-create-groups.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/api/groups.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/api/groups.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/menus/CollectionMenu.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/models/Group.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * minor fixes * Update app/scenes/CollectionMembers/AddGroupsToCollection.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/menus/GroupMenu.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/menus/GroupMenu.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/menus/GroupMenu.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/Collection.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/CollectionMembers/CollectionMembers.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/GroupNew.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/GroupNew.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/Settings/Groups.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/api/documents.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/CollectionMembers/components/CollectionGroupMemberListItem.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * address comments * WIP - getting websocket stuff up and running * socket event for group deletion * wrapped up cascading deletes * lint * flow * fix: UI feedback * fix: Facepile size * fix: Lots of missing await's * Allow clicking facepile on group list item to open members * remove unused route push, grammar * fix: Remove bad analytics events feat: Add group events to audit log * collection. -> collections. * Add groups to entity websocket events (sync create/update/delete) between clients * fix: Users should not be able to see groups they are not a member of * fix: Not caching errors in UI when changing group memberships * fix: Hide unusable UI * test * fix: Tweak language * feat: Automatically open 'add member' modal after creating group Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-03-15 03:48:32 +00:00
await team.removeAdmin(user);
2018-03-04 23:38:51 +00:00
await Event.create({
name: "users.demote",
actorId: ctx.state.user.id,
userId,
teamId,
data: { name: user.name },
ip: ctx.request.ip,
});
2018-03-04 23:38:51 +00:00
ctx.body = {
data: presentUser(user, { includeDetails: true }),
2018-03-04 23:38:51 +00:00
};
});
router.post("users.suspend", auth(), async (ctx) => {
2018-03-04 23:38:51 +00:00
const userId = ctx.body.id;
ctx.assertPresent(userId, "id is required");
2018-03-04 23:38:51 +00:00
const user = await User.findByPk(userId);
authorize(ctx.state.user, "suspend", user);
2018-03-04 23:38:51 +00:00
await userSuspender({
user,
actorId: ctx.state.user.id,
ip: ctx.request.ip,
});
2018-03-04 23:38:51 +00:00
ctx.body = {
data: presentUser(user, { includeDetails: true }),
2018-03-04 23:38:51 +00:00
};
});
router.post("users.activate", auth(), async (ctx) => {
2018-03-04 23:38:51 +00:00
const admin = ctx.state.user;
const userId = ctx.body.id;
const teamId = ctx.state.user.teamId;
ctx.assertPresent(userId, "id is required");
2018-03-04 23:38:51 +00:00
const user = await User.findByPk(userId);
authorize(ctx.state.user, "activate", user);
2018-03-04 23:38:51 +00:00
const team = await Team.findByPk(teamId);
2018-03-04 23:38:51 +00:00
await team.activateUser(user, admin);
await Event.create({
name: "users.activate",
actorId: ctx.state.user.id,
userId,
teamId,
data: { name: user.name },
ip: ctx.request.ip,
});
2018-03-04 23:38:51 +00:00
ctx.body = {
data: presentUser(user, { includeDetails: true }),
2018-03-04 23:38:51 +00:00
};
});
router.post("users.invite", auth(), async (ctx) => {
const { invites } = ctx.body;
ctx.assertPresent(invites, "invites is required");
const user = ctx.state.user;
authorize(user, "invite", User);
2019-12-22 22:54:12 +00:00
const response = await userInviter({ user, invites, ip: ctx.request.ip });
ctx.body = {
2019-12-22 22:54:12 +00:00
data: {
sent: response.sent,
users: response.users.map((user) => presentUser(user)),
2019-12-22 22:54:12 +00:00
},
};
});
router.post("users.delete", auth(), async (ctx) => {
const { confirmation, id } = ctx.body;
ctx.assertPresent(confirmation, "confirmation is required");
2018-07-07 22:38:22 +00:00
let user = ctx.state.user;
if (id) user = await User.findByPk(id);
authorize(ctx.state.user, "delete", user);
2018-07-07 22:38:22 +00:00
feat: Add groups and group permissions (#1204) * WIP - got one API test to pass yay * adds group update endpoint * added group policies * adds groups.list API * adds groups.info * remove comment * WIP * tests for delete * adds group membership list * adds tests for groups list * add and remove user endpoints for group * ask some questions * fix up some issues around primary keys * remove export from group permissions Co-Authored-By: Tom Moor <tom.moor@gmail.com> * remove random file * only create events on actual updates, add tests to ensure * adds uniqueness validation to group name * throw validation errors on model and let it pass through the controller * fix linting * WIP * WIP * WIP * WIP * WIP basic edit and delete * basic CRUD for groups and memberships in place * got member counts working * add member count and limit the number of users sent over teh wire to 6 * factor avatar with AvatarWithPresence into its own class * wip * WIP avatars in group lists * WIP collection groups * add and remove group endpoints * wip add collection groups * wip get group adding to collections to work * wip get updating collection group memberships to work * wip get new group modal working * add tests for collection index * include collection groups in the withmemberships scope * tie permissions to group memberships * remove unused import * Update app/components/GroupListItem.js update title copy Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/migrations/20191211044318-create-groups.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/api/groups.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/api/groups.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/menus/CollectionMenu.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/models/Group.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * minor fixes * Update app/scenes/CollectionMembers/AddGroupsToCollection.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/menus/GroupMenu.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/menus/GroupMenu.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/menus/GroupMenu.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/Collection.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/CollectionMembers/CollectionMembers.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/GroupNew.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/GroupNew.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/Settings/Groups.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update server/api/documents.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * Update app/scenes/CollectionMembers/components/CollectionGroupMemberListItem.js Co-Authored-By: Tom Moor <tom.moor@gmail.com> * address comments * WIP - getting websocket stuff up and running * socket event for group deletion * wrapped up cascading deletes * lint * flow * fix: UI feedback * fix: Facepile size * fix: Lots of missing await's * Allow clicking facepile on group list item to open members * remove unused route push, grammar * fix: Remove bad analytics events feat: Add group events to audit log * collection. -> collections. * Add groups to entity websocket events (sync create/update/delete) between clients * fix: Users should not be able to see groups they are not a member of * fix: Not caching errors in UI when changing group memberships * fix: Hide unusable UI * test * fix: Tweak language * feat: Automatically open 'add member' modal after creating group Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-03-15 03:48:32 +00:00
await user.destroy();
await Event.create({
name: "users.delete",
actorId: user.id,
userId: user.id,
teamId: user.teamId,
data: { name: user.name },
ip: ctx.request.ip,
});
2018-07-07 22:38:22 +00:00
ctx.body = {
success: true,
};
});
2016-07-22 07:11:54 +00:00
export default router;