Skip to content

Commit

Permalink
Minor changes (#193)
Browse files Browse the repository at this point in the history
* added update restrictions, techStacks in project and orgId to a user

* added edit restriction mechanism

* intial deactivation mechanism

* intial logging mechanism and deactive account

* issue fix

* fixing org creation issue

* Fixes issues in testing (#160)

* fix failing test file user.test.js

* fixes EADDRINUSE while testing

* fixes issues in prposal route

* fixed issues in org route and added new tests

* Moving Google Analytics requests to backend (#154)

* Moving Google Analytics requetss to backend

* Requested Changes

* Minor changes

* Adding code to prevent attacks (#153)

* modified user's api (#168)

* modified user's api

* login options during login

* Changes for reactions (#166)

* Revert "Adding code to prevent attacks (#153)" (#170)

This reverts commit 57a0cf9.

* Fixed and written all the missing test cases (#172)

* update code

* fixed failing test cases and missing test cases

* User activity tracker using redis (#174)

* update code

* initial mechanism for user tracking

initial mechanism for user tracking

* Security issue (#177)

* update code

* fix security flaws

* fixed test cases

* minor fixes (#178)

* add docs for new contributors (#181)

Co-authored-by: Devesh Verma <[email protected]>
Co-authored-by: Kumar Saurabh Raj <[email protected]>
Co-authored-by: Asel Peiris <[email protected]>
Co-authored-by: pranjals149 <[email protected]>
Co-authored-by: Vaibhav D. Aren <[email protected]>
  • Loading branch information
6 people authored Sep 13, 2020
1 parent 940a3fa commit fed0e63
Show file tree
Hide file tree
Showing 33 changed files with 993 additions and 256 deletions.
8 changes: 7 additions & 1 deletion .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ JWT_SECRET="thisismysupersecrettokenjustkidding"
DATABASE_URL="mongodb://mongo:27017/donut-development"
SENDGRID_API_KEY='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'
SOCKET_PORT=8810
clientbaseurl = "http://localhost:3000/"
clientbaseurl = "http://localhost:3000"
SOCKET_PORT=8810
REDIS_HOST="redis"
REDIS_PORT=6379
REDIS_PASSWORD="auth"
REDIS_DB=0
REDIS_ACTIVITY_DB=1
126 changes: 93 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,47 +21,91 @@
</a>
</p>

### STEPS

```.sh
npm install
## Prerequisite:

These are the requirement that should be installed locally on your machine.

- Node.js
- MongoDB
- Redis


## How to setup node.js on your machine?

- Move to: [link](https://nodejs.org/en/download/) choose the operating system as per your machine and start downloading and setup by clicking recommended settings in the installation wizard.

## How to setup MongoDB on your machine?

- Move to: [link](https://docs.mongodb.com/manual/administration/install-community/) look at the left sidebar and choose the operating system according to your machine. Then follow the steps recommended in the official docs.

```
Note: You can also use the MongoDB servers like Mlab or MongoDB Cluster server
```

#### Package descriptions
## How to setup redis on your machine?

- bcrypt :- hash your plain password and store hashed password in database.
- body-parser :- Parse incoming request bodies in a middleware before your handlers, available under the req.bodyproperty.
- express :- Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
- jsonwebtoken :- JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
- mongoose :- Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
- morgan :- HTTP request logger middleware for node.js.
- nodemon :- nodemon will watch the files in the directory in which nodemon was started, and if any files change, nodemon will automatically restart your node application.
- Follow the steps provided in the [link](https://auth0.com/blog/introduction-to-redis-install-cli-commands-and-data-types/) to install redis on your operating system

## How to set up this project locally?

These are the Donut APIs
- Move to: https://github.com/codeuino/social-platform-donut-backend
- Fork the repo
- Clone the repo using:
```sh
git clone https://github.com/codeuino/social-platform-donut-backend.git
```
- Now move to the project directory on your machine.
```
cd social-platform-donut-backend
```
- Now use ```git checkout development``` to move to the development branch.
- Install all the dependencies using:
```sh
npm install
```
- Run the development server using:
```sh
npm run dev
```
- Now the server is running on PORT 5000 or the PORT mentioned in the environment **.env.dev** variables

## API Response Format
The response body for all the APIs will use the following format and it will contain one of them ( data | errors ).
<pre>
{
data: {
/../
},
errors: {
/../
}
}
</pre>

## Error Objects
Error objects provide additional information about problems encountered while performing an operation. Error objects MUST be returned as an array keyed by errors in the top level of a JSON:API document.
```
Note: Setup the environment variables as mentioned below
```


## Run unit test
Use the given below command to run all the unit test cases.
```
npm run test
```

### An error object MAY have the following members:

- `status` : the HTTP status code applicable to this problem, expressed as a string value.
- `code` : an application-specific error code, expressed as a string value.
- `title` : a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
- `detail` : a human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized.
## What are the environment variables required to run the project locally on a machine?
- Follow these steps to set the environment variable for development:
- Move to the root directory of the project and open **.env.dev** (for development) or **.env.test** (for testing)
- PORT = 5000
- NODE_ENV = "development"
- JWT_SECRET="<YOUR SECRET KEY>"
- DATABASE_URL="<YOUR DB URL>"
- SENDGRID_API_KEY = '<YOUR SENDGRID API KEY>'
- SOCKET_PORT = 8810

Note: To get **SENDGRID_API_KEY** follow the Sendgrid official [docs](https://sendgrid.com/docs/ui/account-and-settings/api-keys/#creating-an-api-key)

## Workflow (After setup)

Must follow the steps to use the platform:
1. Create an organization - [API](https://docs.codeuino.org/donut-social-networking-platform/rest-apis/organization-api#create-an-organization)
2. Register as an admin - [API](https://docs.codeuino.org/donut-social-networking-platform/rest-apis/post-api#create-a-user)
3. Now login and use the features implemented - [API](https://docs.codeuino.org/donut-social-networking-platform/rest-apis/post-api#login-user)
4. To know more about features please go through the docs - [Docs](https://docs.codeuino.org/donut-social-networking-platform/rest-apis/post-api)

```
NOTE: Please make sure when you setup for the first time your database is empty.
```


## Allowed HTTPs requests:
<pre>
Expand Down Expand Up @@ -122,5 +166,21 @@ DELETE : To delete resource
<td><code>409</code></td>
<td><code>Conflict</code></td>
<td>resourse with given id already exist.</td>
</tr>
<tr>
<td><code>429</code></td>
<td><code>Too many requests</code></td>
<td>sent too many requests to the server in short span of time</td>
</tr>
</table>
</table>
<br></br>

## Contributing 💻
We are happy to see you here and we welcome your contributions towards Donut-Platform.
Contributions are not limited to coding only, you can help in many other ways which includes leaving constructive feedback to people's Pull Request threads also.

Donut platform also provides an extensive list of issues, some of them includes labels like good-first-issue, help-wanted. You can take a look at good-first-issue issues if you are new here but you are free to choose any issue you would like to work on.

If there's no issue available currently, you can setup the project locally and find out the bugs/new features and open issues for that and discuss the bugs or features with the project maintainers or admins.

After choosing an issue and doing changes in the code regarding that, you can open up a Pull Request (PR) to development branch to get your work reviewed and merged!
46 changes: 24 additions & 22 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ const morgan = require('morgan')
const cookieParser = require('cookie-parser')
const createError = require('http-errors')
const path = require('path')

const socket = require('socket.io')
const multer = require('multer')
const bodyParser = require('body-parser')
const cors = require('cors')
const helmet = require('helmet')
const hpp = require('hpp')
var winston = require('./config/winston')
const rateLimiter = require('./app/middleware/rateLimiter')
const sanitizer = require('./app/middleware/sanitise')
const fileConstants = require('./config/fileHandlingConstants')


const indexRouter = require('./app/routes/index')
const authRouter = require('./app/routes/auth')
const usersRouter = require('./app/routes/user')
Expand All @@ -25,13 +27,15 @@ const projectRouter = require('./app/routes/project')
const notificationRouter = require('./app/routes/notification')
const proposalRouter = require('./app/routes/proposal')
const analyticsRouter = require('./app/routes/analytics')
const activityRouter = require('./app/routes/activity')

const app = express()
const server = require('http').Server(app)

app.use(cors())

app.use(bodyParser.json({ limit: '200mb' }))
app.use(cookieParser())
app.use(bodyParser.urlencoded(fileConstants.fileParameters))

const memoryStorage = multer.memoryStorage()
Expand All @@ -49,26 +53,6 @@ io.on('connection', (socket) => {
io.emit('user connected')
})

app.use(helmet());
app.use(hpp());

const csrfMiddleware = csurf({
cookie: true
});

app.use(session({
secret: 'codeuino',
resave: false,
saveUninitialized: true,
cookie: {
secure: true,
httpOnly: true
}
}));

app.use(cookieParser());
app.use(csrfMiddleware);

// view engine setup
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
Expand All @@ -93,6 +77,23 @@ app.use((req, res, next) => {
next()
})

// TO PREVENT DOS ATTACK AND RATE LIMITER
app.use(rateLimiter.customRateLimiter)

// TO PREVENT XSS ATTACK
app.use(sanitizer.cleanBody)
app.use(helmet())

// TO PREVENT CLICK JACKING
app.use((req, res, next) => {
res.append('X-Frame-Options', 'Deny')
res.set('Content-Security-Policy', "frame-ancestors 'none';")
next()
})

// TO PREVENT THE QUERY PARAMETER POLLUTION
app.use(hpp())

app.use('/notification', notificationRouter)
app.use('/', indexRouter)
app.use('/auth', authRouter)
Expand All @@ -105,6 +106,7 @@ app.use('/comment', commentRouter)
app.use('/project', projectRouter)
app.use('/proposal', proposalRouter)
app.use('/analytics', analyticsRouter)
app.use('/activity', activityRouter)

// catch 404 and forward to error handler
app.use(function (req, res, next) {
Expand Down
32 changes: 32 additions & 0 deletions app/controllers/activity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const User = require('../models/User')
const Activity = require('../models/Activity')

const HttpStatus = require('http-status-codes')
module.exports = {

getActivity: async (req, res, next) => {
// userID whose activity will be fetched by admin
const { id } = req.params

try {
// Check if user exists
const user = await User.findById(req.user._id)
if (!user) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'No such user exists!' })
}

// check if not admin
if (user.isAdmin !== true) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'You don\'t have permission!' })
}

const data = await Activity.findOne({ userId: id })
return res.status(HttpStatus.OK).json({ activity: data.activity })
} catch (error) {
if (process.env.NODE_ENV !== 'production') {
console.log(error.name, '-', error.message)
}
return res.status(HttpStatus.BAD_REQUEST).json({ error: error.message })
}
}
}
8 changes: 6 additions & 2 deletions app/controllers/auth.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const User = require('../models/User')
const HttpStatus = require('http-status-codes')
const activityHelper = require('../utils/activity-helper')

module.exports = {
authenticateUser: async (req, res, next) => {
const email = req.body.email
const password = req.body.password
const email = escape(req.body.email)
const password = escape(req.body.password)
try {
const user = await User.findByCredentials(email, password)
const token = await user.generateAuthToken()
Expand All @@ -13,9 +15,11 @@ module.exports = {
}
},
logout: (req, res, next) => {
activityHelper.addActivityToDb(req, res)
res.status(HttpStatus.OK).json({ success: 'ok' })
},
logoutAll: (req, res, next) => {
activityHelper.addActivityToDb(req, res)
res.status(HttpStatus.OK).json({ success: 'ok' })
}
}
12 changes: 8 additions & 4 deletions app/controllers/comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const HttpStatus = require('http-status-codes')
const CommentModel = require('../models/Comment')
const permission = require('../utils/permission')
const helper = require('../utils/paginate')
const activityTracker = require('../utils/activity-helper')
const collectionTypes = require('../utils/collections')

module.exports = {
// CREATE COMMENT (ISSUE IN CREATE COMMENT )
Expand All @@ -14,7 +16,8 @@ module.exports = {
comment.userId = userId
comment.postId = id // added postId
await comment.save()
res.status(HttpStatus.CREATED).json({ comment: comment })
activityTracker.addToRedis(req, res, next, collectionTypes.COMMENT, comment._id)
return res.status(HttpStatus.CREATED).json({ comment: comment })
} catch (error) {
HANDLER.handleError(res, error)
}
Expand Down Expand Up @@ -63,7 +66,8 @@ module.exports = {
comment[update] = req.body[update]
})
await comment.save()
res.status(HttpStatus.OK).json({ comment: comment })
activityTracker.addToRedis(req, res, next, collectionTypes.COMMENT, comment._id)
return res.status(HttpStatus.OK).json({ comment: comment })
} catch (error) {
HANDLER.handleError(res, error)
}
Expand All @@ -78,10 +82,10 @@ module.exports = {
.sort({ updatedAt: -1 })
.lean()
.exec()
if (!comments) {
if (comments === undefined || comments.length === 0) {
return res.status(HttpStatus.NOT_FOUND).json({ error: 'No such post' })
}
res.status(HttpStatus.OK).json({ comments: comments })
return res.status(HttpStatus.OK).json({ comments: comments })
} catch (error) {
HANDLER.handleError(res, error)
}
Expand Down
11 changes: 10 additions & 1 deletion app/controllers/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const permission = require('../utils/permission')
const helper = require('../utils/paginate')
const notificationHelper = require('../utils/notif-helper')
const settingsHelper = require('../utils/settingHelpers')
const activityTracker = require('../utils/activity-helper')
const collectionTypes = require('../utils/collections')

const notification = {
heading: '',
content: '',
Expand All @@ -22,6 +25,7 @@ module.exports = {
notification.content = `${event.eventName} is added!`
notification.tag = 'New!'
notificationHelper.addToNotificationForAll(req, res, notification, next)
activityTracker.addToRedis(req, res, next, collectionTypes.EVENT, event._id)
res.status(HttpStatus.CREATED).json({ event: event })
} catch (error) {
res.status(HttpStatus.BAD_REQUEST).json({ error: error })
Expand Down Expand Up @@ -53,6 +57,7 @@ module.exports = {
notification.content = `${event.eventName} is updated!`
notification.tag = 'Update'
notificationHelper.addToNotificationForAll(req, res, notification, next)
activityTracker.addToRedis(req, res, next, collectionTypes.EVENT, event._id)
res.status(HttpStatus.OK).json({ event: event })
} catch (error) {
HANDLER.handleError(res, error)
Expand Down Expand Up @@ -163,7 +168,11 @@ module.exports = {
notification.content = `Event ${deleteEvent.eventName} is deleted!`
notification.tag = 'Deleted'
notificationHelper.addToNotificationForAll(req, res, notification, next)
return res.status(HttpStatus.OK).json({ deleteEvent: deleteEvent, message: 'Deleted the event' })
activityTracker.addToRedis(req, res, next, collectionTypes.EVENT, deleteEvent._id)
return res.status(HttpStatus.OK).json({
deleteEvent: deleteEvent,
message: 'Deleted the event'
})
}
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'Not permitted!' })
} catch (error) {
Expand Down
Loading

0 comments on commit fed0e63

Please sign in to comment.