diff --git a/server/api/team.js b/server/api/team.js index ff5894288..ea9f817af 100644 --- a/server/api/team.js +++ b/server/api/team.js @@ -1,7 +1,7 @@ // @flow import Router from "koa-router"; import auth from "../middlewares/authentication"; -import { Team } from "../models"; +import { Event, Team } from "../models"; import policy from "../policies"; import { presentTeam, presentPolicies } from "../presenters"; @@ -31,8 +31,24 @@ router.post("team.update", auth(), async (ctx) => { if (documentEmbeds !== undefined) team.documentEmbeds = documentEmbeds; if (guestSignin !== undefined) team.guestSignin = guestSignin; if (avatarUrl !== undefined) team.avatarUrl = avatarUrl; + + const changes = team.changed(); + const data = {}; + await team.save(); + for (const change of changes) { + data[change] = team[change]; + } + + await Event.create({ + name: "teams.update", + actorId: user.id, + teamId: user.teamId, + data, + ip: ctx.request.ip, + }); + ctx.body = { data: presentTeam(team), policies: presentPolicies(user, [team]), diff --git a/server/api/users.js b/server/api/users.js index 7e3b06f70..7f929afd4 100644 --- a/server/api/users.js +++ b/server/api/users.js @@ -69,6 +69,14 @@ router.post("users.update", auth(), async (ctx) => { await user.save(); + await Event.create({ + name: "users.update", + actorId: user.id, + userId: user.id, + teamId: user.teamId, + ip: ctx.request.ip, + }); + ctx.body = { data: presentUser(user, { includeDetails: true }), }; diff --git a/server/auth/google.js b/server/auth/google.js index d7f12639d..d52a96394 100644 --- a/server/auth/google.js +++ b/server/auth/google.js @@ -5,7 +5,7 @@ import Router from "koa-router"; import { capitalize } from "lodash"; import Sequelize from "sequelize"; import auth from "../middlewares/authentication"; -import { User, Team, Event } from "../models"; +import { User, Team } from "../models"; const Op = Sequelize.Op; @@ -122,20 +122,6 @@ router.get("google.callback", auth({ required: false }), async (ctx) => { await team.provisionSubdomain(hostname); } - if (isFirstSignin) { - await Event.create({ - name: "users.create", - actorId: user.id, - userId: user.id, - teamId: team.id, - data: { - name: user.name, - service: "google", - }, - ip: ctx.request.ip, - }); - } - // set cookies on response and redirect to team subdomain ctx.signIn(user, team, "google", isFirstSignin); } catch (err) { diff --git a/server/auth/slack.js b/server/auth/slack.js index 698bb6974..c1915e1ad 100644 --- a/server/auth/slack.js +++ b/server/auth/slack.js @@ -4,14 +4,7 @@ import Router from "koa-router"; import Sequelize from "sequelize"; import { slackAuth } from "../../shared/utils/routeHelpers"; import auth from "../middlewares/authentication"; -import { - Authentication, - Collection, - Integration, - User, - Event, - Team, -} from "../models"; +import { Authentication, Collection, Integration, User, Team } from "../models"; import * as Slack from "../slack"; import { getCookieDomain } from "../utils/domains"; @@ -101,20 +94,6 @@ router.get("slack.callback", auth({ required: false }), async (ctx) => { await team.provisionSubdomain(data.team.domain); } - if (isFirstSignin) { - await Event.create({ - name: "users.create", - actorId: user.id, - userId: user.id, - teamId: team.id, - data: { - name: user.name, - service: "slack", - }, - ip: ctx.request.ip, - }); - } - // set cookies on response and redirect to team subdomain ctx.signIn(user, team, "slack", isFirstSignin); } catch (err) { diff --git a/server/events.js b/server/events.js index 5ed9786b4..f60181c5c 100644 --- a/server/events.js +++ b/server/events.js @@ -9,6 +9,7 @@ const log = debug("services"); export type UserEvent = | { name: | "users.create" // eslint-disable-line + | "users.signin" | "users.update" | "users.suspend" | "users.activate" @@ -16,6 +17,7 @@ export type UserEvent = userId: string, teamId: string, actorId: string, + ip: string, } | { name: "users.invite", @@ -25,6 +27,7 @@ export type UserEvent = email: string, name: string, }, + ip: string, }; export type DocumentEvent = @@ -43,6 +46,7 @@ export type DocumentEvent = collectionId: string, teamId: string, actorId: string, + ip: string, } | { name: "documents.move", @@ -54,6 +58,7 @@ export type DocumentEvent = collectionIds: string[], documentIds: string[], }, + ip: string, } | { name: | "documents.update" // eslint-disable-line @@ -69,6 +74,7 @@ export type DocumentEvent = autosave: boolean, done: boolean, }, + ip: string, } | { name: "documents.title_change", @@ -81,6 +87,7 @@ export type DocumentEvent = title: string, previousTitle: string, }, + ip: string, }; export type RevisionEvent = { @@ -98,6 +105,7 @@ export type CollectionEvent = collectionId: string, teamId: string, actorId: string, + ip: string, } | { name: "collections.add_user" | "collections.remove_user", @@ -105,6 +113,7 @@ export type CollectionEvent = collectionId: string, teamId: string, actorId: string, + ip: string, } | { name: "collections.add_group" | "collections.remove_group", @@ -139,6 +148,15 @@ export type IntegrationEvent = { modelId: string, teamId: string, actorId: string, + ip: string, +}; + +export type TeamEvent = { + name: "teams.update", + teamId: string, + actorId: string, + data: Object, + ip: string, }; export type Event = @@ -147,7 +165,8 @@ export type Event = | CollectionEvent | IntegrationEvent | GroupEvent - | RevisionEvent; + | RevisionEvent + | TeamEvent; const globalEventsQueue = createQueue("global events"); const serviceEventsQueue = createQueue("service events"); diff --git a/server/middlewares/authentication.js b/server/middlewares/authentication.js index 9ed0899b5..126221334 100644 --- a/server/middlewares/authentication.js +++ b/server/middlewares/authentication.js @@ -2,7 +2,7 @@ import addMonths from "date-fns/add_months"; import JWT from "jsonwebtoken"; import { AuthenticationError, UserSuspendedError } from "../errors"; -import { User, Team, ApiKey } from "../models"; +import { User, Event, Team, ApiKey } from "../models"; import type { ContextWithState } from "../types"; import { getCookieDomain } from "../utils/domains"; import { getUserForJWT } from "../utils/jwt"; @@ -94,12 +94,7 @@ export default function auth(options?: { required?: boolean } = {}) { ctx.state.user = user; } - ctx.signIn = async ( - user: User, - team: Team, - service, - isFirstSignin = false - ) => { + ctx.signIn = (user: User, team: Team, service, isFirstSignin = false) => { if (user.isSuspended) { return ctx.redirect("/?notice=suspended"); } @@ -107,6 +102,32 @@ export default function auth(options?: { required?: boolean } = {}) { // update the database when the user last signed in user.updateSignedIn(ctx.request.ip); + if (isFirstSignin) { + Event.create({ + name: "users.create", + actorId: user.id, + userId: user.id, + teamId: team.id, + data: { + name: user.name, + service, + }, + ip: ctx.request.ip, + }); + } else { + Event.create({ + name: "users.signin", + actorId: user.id, + userId: user.id, + teamId: team.id, + data: { + name: user.name, + service, + }, + ip: ctx.request.ip, + }); + } + const domain = getCookieDomain(ctx.request.hostname); const expires = addMonths(new Date(), 3); diff --git a/server/models/Event.js b/server/models/Event.js index d5421e3a6..c0fa2374c 100644 --- a/server/models/Event.js +++ b/server/models/Event.js @@ -55,7 +55,8 @@ Event.add = (event) => { }; Event.ACTIVITY_EVENTS = [ - "users.create", + "collections.create", + "collections.delete", "documents.publish", "documents.archive", "documents.unarchive", @@ -63,20 +64,19 @@ Event.ACTIVITY_EVENTS = [ "documents.unpin", "documents.delete", "documents.restore", - "collections.create", - "collections.delete", + "users.create", ]; Event.AUDIT_EVENTS = [ "api_keys.create", "api_keys.delete", - "users.create", - "users.promote", - "users.demote", - "users.invite", - "users.suspend", - "users.activate", - "users.delete", + "collections.create", + "collections.update", + "collections.add_user", + "collections.remove_user", + "collections.add_group", + "collections.remove_group", + "collections.delete", "documents.create", "documents.publish", "documents.update", @@ -86,19 +86,22 @@ Event.AUDIT_EVENTS = [ "documents.unpin", "documents.move", "documents.delete", - "shares.create", - "shares.update", - "shares.revoke", "groups.create", "groups.update", "groups.delete", - "collections.create", - "collections.update", - "collections.add_user", - "collections.remove_user", - "collections.add_group", - "collections.remove_group", - "collections.delete", + "shares.create", + "shares.update", + "shares.revoke", + "teams.update", + "users.create", + "users.update", + "users.signin", + "users.promote", + "users.demote", + "users.invite", + "users.suspend", + "users.activate", + "users.delete", ]; export default Event;