Skip to content

Implementation v3 #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- nodejs https://nodejs.org/en/ (v10)
- DynamoDB
- AWS S3
- Docker, Docker Compose

## Configuration
Expand All @@ -15,14 +16,38 @@ The following parameters can be set in config files or in env variables:
- PORT: the server port, default is 3000
- AUTH_SECRET: The authorization secret used during token verification.
- VALID_ISSUERS: The valid issuer of tokens.
- DYNAMODB.AWS_ACCESS_KEY_ID: The Amazon certificate key to use when connecting. Use local dynamodb you can set fake value
- DYNAMODB.AWS_SECRET_ACCESS_KEY: The Amazon certificate access key to use when connecting. Use local dynamodb you can set fake value
- DYNAMODB.AWS_REGION: The Amazon certificate region to use when connecting. Use local dynamodb you can set fake value
- DYNAMODB.IS_LOCAL: Use Amazon DynamoDB Local or server.
- DYNAMODB.URL: The local url if using Amazon DynamoDB Local
- AUTH0_URL: AUTH0 URL, used to get M2M token
- AUTH0_PROXY_SERVER_URL: AUTH0 proxy server URL, used to get M2M token
- AUTH0_AUDIENCE: AUTH0 audience, used to get M2M token
- TOKEN_CACHE_TIME: AUTH0 token cache time, used to get M2M token
- AUTH0_CLIENT_ID: AUTH0 client id, used to get M2M token
- AUTH0_CLIENT_SECRET: AUTH0 client secret, used to get M2M token
- AMAZON.AWS_ACCESS_KEY_ID: The Amazon certificate key to use when connecting. Use local dynamodb you can set fake value
- AMAZON.AWS_SECRET_ACCESS_KEY: The Amazon certificate access key to use when connecting. Use local dynamodb you can set fake value
- AMAZON.AWS_REGION: The Amazon certificate region to use when connecting. Use local dynamodb you can set fake value
- AMAZON.IS_LOCAL_DB: Use Amazon DynamoDB Local or server.
- AMAZON.DYNAMODB_URL: The local url if using Amazon DynamoDB Local
- AMAZON.ATTACHMENT_S3_BUCKET: the AWS S3 bucket to store attachments
- FILE_UPLOAD_SIZE_LIMIT: the file upload size limit in bytes
- CHALLENGES_API_URL: TC challenges API base URL
- GROUPS_API_URL: TC groups API base URL
- COPILOT_RESOURCE_ROLE_IDS: copilot resource role ids allowed to upload attachment


Set the following environment variables so that the app can get TC M2M token (use 'set' insted of 'export' for Windows OS):

- export AUTH0_CLIENT_ID=8QovDh27SrDu1XSs68m21A1NBP8isvOt
- export AUTH0_CLIENT_SECRET=3QVxxu20QnagdH-McWhVz0WfsQzA1F8taDdGDI4XphgpEYZPcMTF4lX3aeOIeCzh
- export AUTH0_URL=https://topcoder-dev.auth0.com/oauth/token
- export AUTH0_AUDIENCE=https://m2m.topcoder-dev.com/


Also properly configure AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, ATTACHMENT_S3_BUCKET config parameters.


## DynamoDB Setup with Docker
We will use DynamoDB setup on Docker.
Note that you may need to modify regions in `local/init-dynamodb.sh` and `local/config`.

Just run `docker-compose up` in local folder

Expand All @@ -49,16 +74,27 @@ aws dynamodb scan --table-name ChallengeSetting --endpoint-url http://localhost:
aws dynamodb scan --table-name AuditLog --endpoint-url http://localhost:7777
aws dynamodb scan --table-name Phase --endpoint-url http://localhost:7777
aws dynamodb scan --table-name TimelineTemplate --endpoint-url http://localhost:7777
aws dynamodb scan --table-name Attachment --endpoint-url http://localhost:7777
```

## Local Deployment

- Install dependencies `npm install`
- Run lint `npm run lint`
- Run lint fix `npm run lint:fix`
- Clear and init db `npm run init-db`
- Start app `npm start`
- App is running at `http://localhost:3000`
- Clear and init db `npm run init-db`

## Verification
Refer to the verification document `Verification.md`

## Notes

- after uploading attachments, the returned attachment ids should be used to update challenge;
finally, attachments have challengeId field linking to their challenge,
challenge also have attachments field linking to its attachments,
this will speed up challenge CRUDS operations.

- updated swagger may be viewed and validated at `http://editor.swagger.io/`

8 changes: 7 additions & 1 deletion Verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Postman tests
- import Postman collection and environment in the docs folder to Postman
- Just run the whole test cases under provided environment.
- run tests from up to down in order

## DynamoDB Verification
1. Open a new console and run the command `docker exec -ti dynamodb sh` to use `aws-cli`
Expand All @@ -15,4 +15,10 @@ aws dynamodb scan --table-name ChallengeSetting --endpoint-url http://localhost:
aws dynamodb scan --table-name AuditLog --endpoint-url http://localhost:7777
aws dynamodb scan --table-name Phase --endpoint-url http://localhost:7777
aws dynamodb scan --table-name TimelineTemplate --endpoint-url http://localhost:7777
aws dynamodb scan --table-name Attachment --endpoint-url http://localhost:7777
```

## S3 Verification

Login to AWS Console, S3 service, view the bucket content.

10 changes: 9 additions & 1 deletion app-constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ const prizeTypes = {
Fifth: 'fifth place'
}

const challengeStatuses = {
Draft: 'Draft',
Canceled: 'Canceled',
Active: 'Active',
Completed: 'Completed'
}

module.exports = {
UserRoles,
prizeSetTypes,
prizeTypes
prizeTypes,
challengeStatuses
}
40 changes: 30 additions & 10 deletions app-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ module.exports = (app) => {
next()
})

// add Authenticator check if route has auth
if (def.auth) {
actions.push((req, res, next) => {
if (_.get(req, 'query.token')) {
_.set(req, 'headers.authorization', `Bearer ${_.trim(req.query.token)}`)
}
next()
})
actions.push((req, res, next) => {
if (_.get(req, 'query.token')) {
_.set(req, 'headers.authorization', `Bearer ${_.trim(req.query.token)}`)
}
next()
})

if (def.auth) {
// add Authenticator/Authorization check if route has auth
actions.push((req, res, next) => {
authenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, res, next)
})
Expand All @@ -48,9 +48,9 @@ module.exports = (app) => {
next(new errors.ForbiddenError('M2M is not supported.'))
} else {
req.authUser.userId = String(req.authUser.userId)
// User
// User roles authorization
if (req.authUser.roles) {
if (!helper.checkIfExists(def.access, req.authUser.roles)) {
if (def.access && !helper.checkIfExists(def.access, req.authUser.roles)) {
next(new errors.ForbiddenError('You are not allowed to perform this action!'))
} else {
next()
Expand All @@ -60,6 +60,26 @@ module.exports = (app) => {
}
}
})
} else {
// public API, but still try to authenticate token if provided, but allow missing/invalid token
actions.push((req, res, next) => {
const interceptRes = {}
interceptRes.status = () => interceptRes
interceptRes.json = () => interceptRes
interceptRes.send = () => next()
authenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, interceptRes, next)
})

actions.push((req, res, next) => {
if (!req.authUser) {
next()
} else if (req.authUser.isMachine) {
next(new errors.ForbiddenError('M2M is not supported.'))
} else {
req.authUser.userId = String(req.authUser.userId)
next()
}
})
}

actions.push(method)
Expand Down
4 changes: 4 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ const cors = require('cors')
const HttpStatus = require('http-status-codes')
const logger = require('./src/common/logger')
const interceptor = require('express-interceptor')
const fileUpload = require('express-fileupload')

// setup express app
const app = express()

app.use(cors())
app.use(fileUpload({
limits: { fileSize: config.FILE_UPLOAD_SIZE_LIMIT }
}))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.set('port', config.PORT)
Expand Down
28 changes: 23 additions & 5 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,29 @@ module.exports = {
PORT: process.env.PORT || 3000,
AUTH_SECRET: process.env.AUTH_SECRET || 'mysecret',
VALID_ISSUERS: process.env.VALID_ISSUERS || '["https://api.topcoder-dev.com", "https://api.topcoder.com", "https://topcoder-dev.auth0.com/"]',
DYNAMODB: {

// used to get M2M token
AUTH0_URL: process.env.AUTH0_URL,
AUTH0_PROXY_SERVER_URL: process.env.AUTH0_PROXY_SERVER_URL,
AUTH0_AUDIENCE: process.env.AUTH0_AUDIENCE || 'https://www.topcoder-dev.com',
TOKEN_CACHE_TIME: process.env.TOKEN_CACHE_TIME,
AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID,
AUTH0_CLIENT_SECRET: process.env.AUTH0_CLIENT_SECRET,

AMAZON: {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID || 'FAKE_ACCESS_KEY',
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY || 'FAKE_SECRET_ACCESS_KEY',
AWS_REGION: process.env.AWS_REGION || 'eu-central-1',
IS_LOCAL: process.env.IS_LOCAL || true,
URL: process.env.DYNAMODB_URL || 'http://localhost:7777'
}
AWS_REGION: process.env.AWS_REGION || 'ap-northeast-1',
IS_LOCAL_DB: process.env.IS_LOCAL_DB ? process.env.IS_LOCAL_DB === 'true' : true,
DYNAMODB_URL: process.env.DYNAMODB_URL || 'http://localhost:7777',
ATTACHMENT_S3_BUCKET: process.env.ATTACHMENT_S3_BUCKET || 'my-testing-bucket-12345'
},
// in bytes
FILE_UPLOAD_SIZE_LIMIT: process.env.FILE_UPLOAD_SIZE_LIMIT
? Number(process.env.FILE_UPLOAD_SIZE_LIMIT) : 50 * 1024 * 1024, // 50M
CHALLENGES_API_URL: process.env.CHALLENGES_API_URL || 'http://localhost:4000/v5/challenges',
GROUPS_API_URL: process.env.GROUPS_API_URL || 'http://localhost:4000/v5/groups',
// copilot resource role ids allowed to upload attachment
COPILOT_RESOURCE_ROLE_IDS: process.env.COPILOT_RESOURCE_ROLE_IDS
? process.env.COPILOT_RESOURCE_ROLE_IDS.split(',') : ['10ba038e-48da-487b-96e8-8d3b99b6d18b']
}
Loading