diff --git a/.circleci/config.yml b/.circleci/config.yml index eed75ca..b2317b3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -82,7 +82,7 @@ workflows: - "build-dev": filters: branches: - only: dev + only: [dev, 'feature/m2mtoken'] - "build-prod": filters: branches: diff --git a/README.md b/README.md index c4af032..110b135 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,6 @@ The following parameters can be set in config files or in env variables: - `TC_API_V3_BASE_URL`: the TopCoder API V3 base URL - `TC_API_V4_BASE_URL`: the TopCoder API V4 base URL - `MESSAGE_API_BASE_URL`: the TopCoder message service API base URL - - `TC_ADMIN_TOKEN`: the admin token to access TopCoder API - same for V3 and V4 - **Topcder specific**
Also it has probably temporary variables of TopCoder role ids for 'Connect Manager', 'Connect Copilot' and 'administrator': - `CONNECT_MANAGER_ROLE_ID`: 8, @@ -94,21 +93,6 @@ so we can run `node test/token 305384` to generate a token to manage notificatio The generated token is already configured in the Postman notification server API environment TOKEN variable. You may reuse it during review. - -## TC API Admin Token - -An admin token is needed to access TC API. This is already configured Postman notification -server API environment TC_ADMIN_TOKEN variable. -In case it expires, you may get a new token in this way: - -- use Chrome to browse connect.topcoder-dev.com -- open developer tools, click the Network tab -- log in with suser1 / Topcoder123, or mess / appirio123 -- once logged in, open some project, for example https://connect.topcoder-dev.com/projects/1936 and in the network inspector - look for the call to the project api and get the token from the auth header, see - http://pokit.org/get/img/68cdd34f3d205d6d9bd8bddb07bdc216.jpg - - ## Local deployment - for local development environment you can set variables as following: - `AUTH_SECRET`,`VALID_ISSUERS` can get from [tc-project-service config](https://github.com/topcoder-platform/tc-project-service/blob/dev/config/default.json) @@ -116,7 +100,6 @@ In case it expires, you may get a new token in this way: - `KAFKA_TOPIC_IGNORE_PREFIX=joan-26673.` (with point at the end) - `TC_API_V4_BASE_URL=https://api.topcoder-dev.com/v4` - `TC_API_V3_BASE_URL=https://api.topcoder-dev.com/v3` - - `TC_ADMIN_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoic3VzZXIxIiwiZXhwIjoxNTEzNDAxMjU4LCJ1c2VySWQiOiI0MDE1MzkzOCIsImlhdCI6MTUwOTYzNzYzOSwiZW1haWwiOiJtdHdvbWV5QGJlYWtzdGFyLmNvbSIsImp0aSI6IjIzZTE2YjA2LWM1NGItNDNkNS1iY2E2LTg0ZGJiN2JiNDA0NyJ9.REds35fdBvY7CMDGGFyT_tOD7DxGimFfVzIyEy9YA0Y` or follow section **TC API Admin Token** to obtain a new one if expired - `KAFKA_URL`, `KAFKA_CLIENT_CERT` and `KAFKA_CLIENT_CERT_KEY` get from [tc-bus-api readme](https://github.com/topcoder-platform/tc-bus-api/tree/dev) - if you are willing to use notifications API which is hosted by the notifications server locally, you will need to use some patched `tc-core-library-js` module, which skips verification of user token. Because we don't know Topcoder `AUTH_SECRET` locally. So you can install this fork: ``` diff --git a/config/default.js b/config/default.js index 73bf19b..731a569 100644 --- a/config/default.js +++ b/config/default.js @@ -21,7 +21,6 @@ module.exports = { VALID_ISSUERS: process.env.validIssuers ? process.env.validIssuers.replace(/\\"/g, '') : null, // keep it here for dev purposes, it's only needed by modified version of tc-core-library-js // which skips token validation when locally deployed - JWKS_URI: process.env.jwksUri, KAFKA_URL: process.env.KAFKA_URL, KAFKA_TOPIC_IGNORE_PREFIX: process.env.KAFKA_TOPIC_IGNORE_PREFIX, diff --git a/connect/config.js b/connect/config.js index cbff96f..7ee00d7 100644 --- a/connect/config.js +++ b/connect/config.js @@ -7,7 +7,6 @@ module.exports = { TC_API_V3_BASE_URL: process.env.TC_API_V3_BASE_URL || 'https://api.topcoder-dev.com/v3', TC_API_V4_BASE_URL: process.env.TC_API_V4_BASE_URL || 'https://api.topcoder-dev.com/v4', MESSAGE_API_BASE_URL: process.env.MESSAGE_API_BASE_URL || 'https://api.topcoder-dev.com/v5', - TC_ADMIN_TOKEN: process.env.TC_ADMIN_TOKEN, // Probably temporary variables for TopCoder role ids for 'Connect Manager', 'Connect Copilot' and 'administrator' // These are values for development backend. For production backend they may be different. diff --git a/connect/service.js b/connect/service.js index c0cb0ce..20948ef 100644 --- a/connect/service.js +++ b/connect/service.js @@ -14,26 +14,32 @@ const { logger } = require('../index'); * * @return {Promise} promise resolved to project details */ -const getProject = (projectId) => request - .get(`${config.TC_API_V4_BASE_URL}/projects/${projectId}`) - .set('accept', 'application/json') - .set('authorization', `Bearer ${config.TC_ADMIN_TOKEN}`) - .then((res) => { - if (!_.get(res, 'body.result.success')) { - throw new Error(`Failed to get project details of project id: ${projectId}`); - } - - const project = _.get(res, 'body.result.content'); - - return project; - }).catch((err) => { - const errorDetails = _.get(err, 'response.body.result.content.message'); - throw new Error( - `Failed to get project details of project id: ${projectId}.` + - (errorDetails ? ' Server response: ' + errorDetails : '') - ); - }); - +const getProject = (projectId) => { + return M2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) + .then((token) => { + return request + .get(`${config.TC_API_V4_BASE_URL}/projects/${projectId}`) + .set('accept', 'application/json') + .set('authorization', `Bearer ${token}`) + .then((res) => { + if (!_.get(res, 'body.result.success')) { + throw new Error(`Failed to get project details of project id: ${projectId}`); + } + const project = _.get(res, 'body.result.content'); + return project; + }).catch((err) => { + const errorDetails = _.get(err, 'response.body.result.content.message'); + throw new Error( + `Failed to get project details of project id: ${projectId}.` + + (errorDetails ? ' Server response: ' + errorDetails : '') + ); + }); + }) + .catch((err) => { + err.message = 'Error generating m2m token: ' + err.message; + throw err; + }); +}; /** * Get role members * @@ -41,25 +47,32 @@ const getProject = (projectId) => request * * @return {Promise} promise resolved to role members ids list */ -const getRoleMembers = (roleId) => request - .get(`${config.TC_API_V3_BASE_URL}/roles/${roleId}?fields=subjects`) - .set('accept', 'application/json') - .set('authorization', `Bearer ${config.TC_ADMIN_TOKEN}`) - .then((res) => { - if (!_.get(res, 'body.result.success')) { - throw new Error(`Failed to get role members of role id: ${roleId}`); - } - - const members = _.get(res, 'body.result.content.subjects'); - - return members; - }).catch((err) => { - const errorDetails = _.get(err, 'response.body.result.content.message'); - throw new Error( - `Failed to get role members of role id: ${roleId}.` + - (errorDetails ? ' Server response: ' + errorDetails : '') - ); - }); +const getRoleMembers = (roleId) => { + return M2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) + .then((token) => { + return request + .get(`${config.TC_API_V3_BASE_URL}/roles/${roleId}?fields=subjects`) + .set('accept', 'application/json') + .set('authorization', `Bearer ${token}`) + .then((res) => { + if (!_.get(res, 'body.result.success')) { + throw new Error(`Failed to get role membrs of role id: ${roleId}`); + } + const members = _.get(res, 'body.result.content.subjects'); + return members; + }).catch((err) => { + const errorDetails = _.get(err, 'response.body.result.content.message'); + throw new Error( + `Failed to get role membrs of role id: ${roleId}.` + + (errorDetails ? ' Server response: ' + errorDetails : '') + ); + }); + }) + .catch((err) => { + err.message = 'Error generating m2m token: ' + err.message; + throw err; + }); +}; /** * Get users details by ids @@ -76,8 +89,6 @@ const getUsersById = (ids) => { throw err; }) .then((token) => { - if (!token && config.TC_ADMIN_TOKEN) token = config.TC_ADMIN_TOKEN; - return request .get(`${config.TC_API_V3_BASE_URL}/members/_search?fields=userId,email,handle,firstName,lastName&query=${query}`) .set('accept', 'application/json') @@ -115,8 +126,6 @@ const getUsersByHandle = (handles) => { throw err; }) .then((token) => { - if (!token && config.TC_ADMIN_TOKEN) token = config.TC_ADMIN_TOKEN; - return request .get(`${config.TC_API_V3_BASE_URL}/members/_search?fields=userId,handle,firstName,lastName&query=${query}`) .set('accept', 'application/json') @@ -146,26 +155,34 @@ const getUsersByHandle = (handles) => { * * @return {Promise} promise resolved to topic details */ -const getTopic = (topicId) => request - .get(`${config.MESSAGE_API_BASE_URL}/topics/${topicId}/read`) - .set('accept', 'application/json') - .set('authorization', `Bearer ${config.TC_ADMIN_TOKEN}`) - .then((res) => { - if (!_.get(res, 'body.result.success')) { - throw new Error(`Failed to get topic details of topic id: ${topicId}`); - } - - return _.get(res, 'body.result.content'); - }).catch((err) => { - if (logger) { - logger.error(err, `Error while calling ${config.MESSAGE_API_BASE_URL}/topics/${topicId}/read`); - } - const errorDetails = _.get(err, 'response.body.result.content.message'); - throw new Error( - `Failed to get topic details of topic id: ${topicId}.` + - (errorDetails ? ' Server response: ' + errorDetails : '') - ); - }); +const getTopic = (topicId, logger) => { + return M2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) + .then((token) => { + return request + .get(`${config.MESSAGE_API_BASE_URL}/topics/${topicId}/read`) + .set('accept', 'application/json') + .set('authorization', `Bearer ${token}`) + .then((res) => { + if (!_.get(res, 'body.result.success')) { + throw new Error(`Failed to get topic details of topic id: ${topicId}`); + } + return _.get(res, 'body.result.content'); + }).catch((err) => { + if (logger) { + logger.error(err, `Error while calling ${config.MESSAGE_API_BASE_URL}/topics/${topicId}/read`); + } + const errorDetails = _.get(err, 'response.body.result.content.message'); + throw new Error( + `Failed to get topic details of topic id: ${topicId}.` + + (errorDetails ? ' Server response: ' + errorDetails : '') + ); + }); + }) + .catch((err) => { + err.message = 'Error generating m2m token: ' + err.message; + throw err; + }); +}; module.exports = { getProject, diff --git a/deploy.sh b/deploy.sh index 393de4a..f24ff24 100755 --- a/deploy.sh +++ b/deploy.sh @@ -37,11 +37,8 @@ KAFKA_GROUP_ID=$(eval "echo \$${ENV}_KAFKA_GROUP_ID") KAFKA_TOPIC_IGNORE_PREFIX=$(eval "echo \$${ENV}_KAFKA_TOPIC_IGNORE_PREFIX") KAFKA_URL=$(eval "echo \$${ENV}_KAFKA_URL") AUTHSECRET=$(eval "echo \$${ENV}_AUTHSECRET") -AUTHDOMAIN=$(eval "echo \$${ENV}_AUTHDOMAIN") VALID_ISSUERS=$(eval "echo \$${ENV}_VALID_ISSUERS") -JWKSURI=$(eval "echo \$${ENV}_JWKSURI") TC_API_BASE_URL=$(eval "echo \$${ENV}_TC_API_BASE_URL") -TC_ADMIN_TOKEN=$(eval "echo \$${ENV}_TC_ADMIN_TOKEN") LOG_LEVEL=$(eval "echo \$${ENV}_LOG_LEVEL") PORT=$(eval "echo \$${ENV}_PORT") @@ -155,14 +152,6 @@ make_task_def(){ "name": "authSecret", "value": "%s" }, - { - "name": "authDomain", - "value": "%s" - }, - { - "name": "jwksUri", - "value": "%s" - }, { "name": "TC_API_BASE_URL", "value": "%s" @@ -183,10 +172,6 @@ make_task_def(){ "name": "MESSAGE_API_BASE_URL", "value": "%s" }, - { - "name": "TC_ADMIN_TOKEN", - "value": "%s" - }, { "name": "ENABLE_EMAILS", "value": "%s" @@ -266,7 +251,7 @@ make_task_def(){ } ]' - task_def=$(printf "$task_template" $AWS_ECS_CONTAINER_NAME $AWS_ACCOUNT_ID $AWS_REGION $AWS_REPOSITORY $TAG $ENV "$KAFKA_CLIENT_CERT" "$KAFKA_CLIENT_CERT_KEY" $KAFKA_GROUP_ID "$KAFKA_TOPIC_IGNORE_PREFIX" $KAFKA_URL $DATABASE_URL $AUTHSECRET "$AUTHDOMAIN" "$JWKSURI" $TC_API_BASE_URL $TC_API_V3_BASE_URL $TC_API_V4_BASE_URL $TC_API_V5_BASE_URL $MESSAGE_API_BASE_URL $TC_ADMIN_TOKEN $ENABLE_EMAILS $MENTION_EMAIL $REPLY_EMAIL_PREFIX $REPLY_EMAIL_DOMAIN $ENABLE_DEV_MODE $DEV_MODE_EMAIL $LOG_LEVEL $VALID_ISSUERS $PORT "$API_CONTEXT_PATH" "$AUTH0_URL" "$AUTH0_AUDIENCE" $AUTH0_CLIENT_ID "$AUTH0_CLIENT_SECRET" $TOKEN_CACHE_TIME $AWS_ECS_CLUSTER $AWS_REGION $AWS_ECS_CLUSTER $ENV) + task_def=$(printf "$task_template" $AWS_ECS_CONTAINER_NAME $AWS_ACCOUNT_ID $AWS_REGION $AWS_REPOSITORY $TAG $ENV "$KAFKA_CLIENT_CERT" "$KAFKA_CLIENT_CERT_KEY" $KAFKA_GROUP_ID "$KAFKA_TOPIC_IGNORE_PREFIX" $KAFKA_URL $DATABASE_URL $AUTHSECRET $TC_API_BASE_URL $TC_API_V3_BASE_URL $TC_API_V4_BASE_URL $TC_API_V5_BASE_URL $MESSAGE_API_BASE_URL $ENABLE_EMAILS $MENTION_EMAIL $REPLY_EMAIL_PREFIX $REPLY_EMAIL_DOMAIN $ENABLE_DEV_MODE $DEV_MODE_EMAIL $LOG_LEVEL $VALID_ISSUERS $PORT "$API_CONTEXT_PATH" "$AUTH0_URL" "$AUTH0_AUDIENCE" $AUTH0_CLIENT_ID "$AUTH0_CLIENT_SECRET" $TOKEN_CACHE_TIME $AWS_ECS_CLUSTER $AWS_REGION $AWS_ECS_CLUSTER $ENV) } register_definition() {