Skip to content

Commit

Permalink
login redirect, disallow inviting current member, delete file from S3
Browse files Browse the repository at this point in the history
  • Loading branch information
tima101 committed Jun 22, 2018
1 parent a9442c9 commit 7cb3551
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 92 deletions.
2 changes: 1 addition & 1 deletion api/server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ server.get('/uploaded-file', async (req, res) => {
return;
}

if (teamSlug && teamSlug !== 'temporary') {
if (teamSlug) {
const team = await Team.findOne({ slug: teamSlug })
.select('memberIds')
.lean();
Expand Down
33 changes: 29 additions & 4 deletions api/server/aws-s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ async function checkPrefix(prefix, user) {
}

async function signRequestForUpload({ fileName, fileType, prefix, bucket, user, acl = 'private' }) {
if (prefix !== 'temporary') {
await checkPrefix(prefix, user);
}
await checkPrefix(prefix, user);

aws.config.update({
region: 'us-west-1',
Expand Down Expand Up @@ -101,4 +99,31 @@ function signRequestForLoad(path, bucket) {
});
}

export { signRequestForUpload, signRequestForLoad };
function deleteFiles(bucket: string, files: string[]) {
aws.config.update({
region: 'us-west-1',
accessKeyId: process.env.Amazon_accessKeyId,
secretAccessKey: process.env.Amazon_secretAccessKey,
});

const s3 = new aws.S3({ apiVersion: 'latest' });

const params = {
Bucket: bucket,
Delete: {
Objects: files.map(f => ({ Key: f })),
},
};

return new Promise((resolve, reject) => {
s3.deleteObjects(params, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
}

export { signRequestForUpload, signRequestForLoad, deleteFiles };
14 changes: 7 additions & 7 deletions api/server/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,21 @@ export default function auth({ ROOT_URL, server }) {
);
}

let redirectUrlAfterLogin;

if (req.user && req.user.isAdmin) {
res.redirect(`${URL_APP}/admin`);
redirectUrlAfterLogin = `/admin`;
} else if (req.session.next_url) {
let redirectUrlAfterLogin;
redirectUrlAfterLogin = req.session.next_url;
res.redirect(`${URL_APP}${redirectUrlAfterLogin}`);
} else {
let redirectUrlAfterLogin;
if (!req.user.defaultTeamSlug) {
redirectUrlAfterLogin = 'settings/create-team';
redirectUrlAfterLogin = '/settings/create-team';
} else {
redirectUrlAfterLogin = `team/${req.user.defaultTeamSlug}/t/projects`;
redirectUrlAfterLogin = `/team/${req.user.defaultTeamSlug}/t/projects`;
}
res.redirect(`${URL_APP}/${redirectUrlAfterLogin}`);
}

res.redirect(`${URL_APP}${redirectUrlAfterLogin}`);
},
);

Expand Down
8 changes: 7 additions & 1 deletion api/server/models/Discussion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { uniq } from 'lodash';
import { generateNumberSlug } from '../utils/slugify';
import Topic from './Topic';
import Team from './Team';
import Post from './Post';
import Post, { deletePostFiles } from './Post';

const mongoSchema = new mongoose.Schema({
createdUserId: {
Expand Down Expand Up @@ -200,6 +200,12 @@ class DiscussionClass extends mongoose.Model {

await this.checkPermission({ userId, topicId: discussion.topicId });

deletePostFiles(
await Post.find({ discussionId: id })
.select('content')
.lean(),
);

await Post.remove({ discussionId: id });

await this.remove({ _id: id });
Expand Down
9 changes: 6 additions & 3 deletions api/server/models/Invitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,13 @@ class InvitationClass extends mongoose.Model {
throw new Error('Team does not exist or you have no permission');
}

const registeredUser = await User.findOne({ email }).lean();
const registeredUser = await User.findOne({ email })
.select('defaultTeamSlug')
.lean();

if (registeredUser) {
if (team.memberIds.includes(registeredUser._id)) {
throw new Error('Already team member');
if (team.memberIds.includes(registeredUser._id.toString())) {
throw new Error('This user is already Team Member.');
} else {
await Team.update({ _id: team._id }, { $addToSet: { memberIds: registeredUser._id } });

Expand Down
44 changes: 41 additions & 3 deletions api/server/models/Post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,46 @@ import * as marked from 'marked';
import * as he from 'he';
import * as hljs from 'highlight.js';

import * as url from 'url';
import * as qs from 'querystring';
import { chunk } from 'lodash';

import Topic from './Topic';
import Team from './Team';
import Discussion from './Discussion';
import logger from '../logs';
import { deleteFiles } from '../aws-s3';
// import logger from '../logs';

function deletePostFiles(posts: IPostDocument[]) {
const imgRegEx = /\<img.+data-src=[\"|\'](.+?)[\"|\']/g;
const files: { [key: string]: string[] } = {};

posts.forEach(post => {
let res = imgRegEx.exec(post.content);

while (res) {
const { bucket, path } = qs.parse(url.parse(res[1]).query);

if (typeof bucket !== 'string' || typeof path !== 'string') {
continue;
}

if (!files[bucket]) {
files[bucket] = [];
}

files[bucket].push(path);

res = imgRegEx.exec(post.content);
}
});

Object.keys(files).forEach(bucket => {
chunk(files[bucket], 1000).forEach(fileList =>
deleteFiles(bucket, fileList).catch(err => console.log(err)),
);
});
}

const mongoSchema = new mongoose.Schema({
createdUserId: {
Expand Down Expand Up @@ -222,11 +258,13 @@ class PostClass extends mongoose.Model {
}

const post = await this.findById(id)
.select('createdUserId discussionId')
.select('createdUserId discussionId content')
.lean();

await this.checkPermission({ userId, discussionId: post.discussionId, post });

deletePostFiles([post]);

await this.remove({ _id: id });
}
}
Expand All @@ -236,4 +274,4 @@ mongoSchema.loadClass(PostClass);
const Post = mongoose.model<IPostDocument, IPostModel>('Post', mongoSchema);

export default Post;
export { IPostDocument };
export { IPostDocument, deletePostFiles };
8 changes: 7 additions & 1 deletion api/server/models/Topic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { uniq } from 'lodash';
import { generateNumberSlug } from '../utils/slugify';
import Team from './Team';
import Discussion from './Discussion';
import Post from './Post';
import Post, { deletePostFiles } from './Post';

const mongoSchema = new mongoose.Schema({
createdUserId: {
Expand Down Expand Up @@ -174,6 +174,12 @@ class TopicClass extends mongoose.Model {

const discussionIds = discussions.map(d => d._id);

deletePostFiles(
await Post.find({ discussionId: { $in: discussionIds } })
.select('content')
.lean(),
);

await Post.remove({ discussionId: discussionIds });
await Discussion.remove({ _id: discussionIds });

Expand Down
5 changes: 0 additions & 5 deletions api/server/models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ const mongoSchema = new mongoose.Schema({
default: '',
},

// TODO: Is this field really necessary? If so we need to maintain it.
// There is no code that adding/removing teamId, when necessary
teamIds: [String],

projectIds: [String],

isAdmin: {
Expand Down Expand Up @@ -122,7 +118,6 @@ class UserClass extends mongoose.Model {
'slug',
'isAdmin',
'isGithubConnected',
'teamIds',
'defaultTeamSlug',
];
}
Expand Down
6 changes: 4 additions & 2 deletions app/components/common/LoginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';

import Button from '@material-ui/core/Button';
import env from '../../lib/env';
import { makeQueryString } from '../../lib/api/makeQueryString';

import { styleLoginButton } from '../../lib/sharedStyles';

Expand All @@ -16,9 +17,10 @@ class LoginButton extends React.PureComponent<{ next?: string; invitationToken?:
const { next, invitationToken } = this.props;

let url = `${LOGIN_URL}/auth/google`;
const qs = makeQueryString({ next, invitationToken });

if (next && invitationToken) {
url += `?next=${next}&invitationToken=${invitationToken}`;
if (qs) {
url += `?${qs}`;
}

return (
Expand Down
9 changes: 3 additions & 6 deletions app/components/common/MenuWithMenuItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ class MenuWithMenuItems extends React.PureComponent<{

return (
<span>
{/* <Tooltip
<Tooltip
title={menuOptions.tooltipTitle}
placement="top"
disableFocusListener
disableTouchListener
> */}
>
<a href="#" style={{ float: 'right' }} onClick={this.handleClick}>
<i
// aria-owns={menuOptions.ariaOwns}
data-id={menuOptions.dataId}
aria-haspopup="true"
color="action"
Expand All @@ -43,9 +42,7 @@ class MenuWithMenuItems extends React.PureComponent<{
more_vert
</i>
</a>
{/* </Tooltip> */}

{/* Bug of MUI: https://github.com/mui-org/material-ui/issues/11913 */}
</Tooltip>

<Menu
id={menuOptions.id}
Expand Down
11 changes: 11 additions & 0 deletions app/lib/api/makeQueryString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function makeQueryString(params) {
const esc = encodeURIComponent;
const query = Object.keys(params)
.filter(k => !!params[k])
.map(k => `${esc(k)}=${esc(params[k])}`)
.join('&');

return query;
}

export { makeQueryString };
9 changes: 1 addition & 8 deletions app/lib/api/sendRequestAndGetResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@ import 'isomorphic-unfetch';

import getRootUrl from './getRootUrl';

function makeQueryString(params) {
const esc = encodeURIComponent;
const query = Object.keys(params)
.map(k => `${esc(k)}=${params[k] ? esc(params[k]) : ''}`)
.join('&');

return query;
}
import { makeQueryString } from './makeQueryString';

export default async function sendRequestAndGetResponse(path, opts: any = {}) {
const { externalServer } = opts;
Expand Down
2 changes: 1 addition & 1 deletion app/lib/store/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class User {
@observable email: string | null;
@observable displayName: string | null;
@observable avatarUrl: string | null;
@observable isGithubConnected: boolean;
@observable defaultTeamSlug: string;

constructor(params) {
Object.assign(this, params);
Expand Down
17 changes: 15 additions & 2 deletions app/lib/withAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,30 @@ export default function withAuth(
const { store } = this.props;

const user = store.currentUser;

if (loginRequired && !logoutRequired && !user) {
Router.push('/login');
return;
}

let redirectUrl = '/login';
let asUrl = '/login';
if (user) {
if (!user.defaultTeamSlug) {
redirectUrl = '/settings/create-team';
asUrl = '/settings/create-team';
} else {
redirectUrl = `/topics/detail?teamSlug=${user.defaultTeamSlug}&topicSlug=projects`;
asUrl = `/team/${user.defaultTeamSlug}/t/projects`;
}
}

if (adminRequired && (!user || !user.isAdmin)) {
Router.push('/');
Router.push(redirectUrl, asUrl);
}

if (logoutRequired && user) {
Router.push('/');
Router.push(redirectUrl, asUrl);
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"coverageDirectory": "./.coverage"
},
"dependencies": {
"@material-ui/core": "^1.2.2",
"@material-ui/core": "^1.2.3",
"@types/node": "^10.3.3",
"@zeit/next-typescript": "^0.1.1",
"downshift": "^1.31.16",
Expand Down
Loading

0 comments on commit 7cb3551

Please sign in to comment.