Skip to content

Commit 8dcdb17

Browse files
Merge pull request #5462 from topcoder-platform/update-segment-script
Update segment script
2 parents 03dbc9a + bece9cf commit 8dcdb17

File tree

7 files changed

+60
-8
lines changed

7 files changed

+60
-8
lines changed

.circleci/config.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,6 @@ workflows:
276276
branches:
277277
only:
278278
- develop
279-
- remove-outage-banner
280279
# This is alternate dev env for parallel testing
281280
- "build-test":
282281
context : org-global
@@ -306,7 +305,6 @@ workflows:
306305
branches:
307306
only:
308307
- develop
309-
- remove-outage-banner
310308
- "approve-smoke-test-on-staging":
311309
type: approval
312310
requires:

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ ARG MAILCHIMP_BASE_URL
5252
ARG NODE_CONFIG_ENV
5353
ARG OPEN_EXCHANGE_RATES_KEY
5454
ARG SEGMENT_IO_API_KEY
55+
ARG CHAMELEON_VERIFICATION_SECRET
5556
ARG SERVER_API_KEY
5657

5758
# TC M2M credentials for Community App server
@@ -108,6 +109,7 @@ ENV MAILCHIMP_BASE_URL=$MAILCHIMP_BASE_URL
108109
ENV NODE_CONFIG_ENV=$NODE_CONFIG_ENV
109110
ENV OPEN_EXCHANGE_RATES_KEY=$OPEN_EXCHANGE_RATES_KEY
110111
ENV SEGMENT_IO_API_KEY=$SEGMENT_IO_API_KEY
112+
ENV CHAMELEON_VERIFICATION_SECRET=$CHAMELEON_VERIFICATION_SECRET
111113
ENV SERVER_API_KEY=$SERVER_API_KEY
112114

113115
# TC M2M credentials for Community App server

build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ docker build -t $TAG \
3232
--build-arg NODE_CONFIG_ENV=$NODE_CONFIG_ENV \
3333
--build-arg OPEN_EXCHANGE_RATES_KEY=$OPEN_EXCHANGE_RATES_KEY \
3434
--build-arg SEGMENT_IO_API_KEY=$SEGMENT_IO_API_KEY \
35+
--build-arg CHAMELEON_VERIFICATION_SECRET=$CHAMELEON_VERIFICATION_SECRET \
3536
--build-arg SERVER_API_KEY=$SERVER_API_KEY \
3637
--build-arg TC_M2M_CLIENT_ID=$TC_M2M_CLIENT_ID \
3738
--build-arg TC_M2M_CLIENT_SECRET=$TC_M2M_CLIENT_SECRET \

config/custom-environment-variables.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ module.exports = {
9999
RECRUITCRM_API_KEY: 'RECRUITCRM_API_KEY',
100100
GROWSURF_API_KEY: 'GROWSURF_API_KEY',
101101
SENDGRID_API_KEY: 'SENDGRID_API_KEY',
102+
CHAMELEON_VERIFICATION_SECRET: 'CHAMELEON_VERIFICATION_SECRET',
102103
},
103104
GROWSURF_CAMPAIGN_ID: 'GROWSURF_CAMPAIGN_ID',
104105
AUTH_CONFIG: {

docs/secure-identity-verification.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## Setup
2+
1. Make sure you have a Chameleon Account and segment.com account
3+
2. Integrate Chameleon Account with Segment. https://help.trychameleon.com/en/articles/1161770-installing-using-segment
4+
3. Set Environment secret variable retrieved here https://app.trychameleon.com/settings/integrations/segment. Run the following command
5+
`export CHAMELEON_VERIFICATION_SECRET=<Your Chameleon Secret>`
6+
4. Run community app
7+
8+
## Verification
9+
1. Log in to topcoder-dev account
10+
2. Access http://local.topcoder-dev.com/challenges
11+
3. You will notice in the network tab there will be 2 requests POST to https://api.segment.io/v1/i, one will send it to segment and one will send only to chameleon (with request payload `{ integrations: { All: false, Chameleon: true }}`)
12+
13+
Repeat the proses and log in to different account and make sure the `uid_hash` is different for each different user.

src/client/index.jsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ const { setErrorsStore } = errors;
2323
* Performs AnalyticsJS identification of the user.
2424
* @param {Object} profile TC user profile.
2525
* @param {Array} roles User roles.
26+
* @param {String} userIdHash Unique Hash per user.
2627
*/
27-
function identify(profile, roles) {
28-
analytics.identify(profile.userId, {
28+
function identify(profile, roles, userIdHash) {
29+
const payload = {
2930
avatar: profile.photoURL,
3031
createdAt: profile.createdAt,
3132
email: profile.email,
@@ -39,7 +40,21 @@ function identify(profile, roles) {
3940
})),
4041
tracks: profile.tracks || [],
4142
username: profile.handle,
42-
});
43+
};
44+
analytics.identify(
45+
profile.userId,
46+
payload,
47+
{
48+
integrations: { Chameleon: false },
49+
},
50+
);
51+
analytics.identify(
52+
profile.userId,
53+
{ uid_hash: userIdHash, ...payload },
54+
{
55+
integrations: { All: false, Chameleon: true },
56+
},
57+
);
4358
}
4459

4560
/**
@@ -74,7 +89,7 @@ function authenticate(store) {
7489
}).then(({ tctV2, tctV3 }) => {
7590
const { auth } = store.getState();
7691
if (auth.profile && !analyticsIdentitySet) {
77-
identify(auth.profile, _.get(auth, 'user.roles'));
92+
identify(auth.profile, _.get(auth, 'user.roles'), auth.userIdHash);
7893
analyticsIdentitySet = true;
7994
}
8095
if (auth.tokenV3 !== (tctV3 || null)) {
@@ -85,7 +100,7 @@ function authenticate(store) {
85100
const userId = profile && profile.userId;
86101
const prevUserId = _.get(store.getState(), 'auth.profile.userId');
87102
if (userId && userId !== prevUserId) {
88-
identify(profile, _.get(auth, user.roles));
103+
identify(profile, _.get(auth, user.roles), auth.userIdHash);
89104
analyticsIdentitySet = true;
90105
}
91106
});

src/shared/reducers/index.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
*/
1616

1717
import _ from 'lodash';
18+
import crypto from 'crypto';
1819
import { getCommunityId } from 'server/services/communities';
19-
import { redux } from 'topcoder-react-utils';
20+
import { redux, config, isomorphy } from 'topcoder-react-utils';
2021
import { reducer as toastrReducer } from 'react-redux-toastr';
2122
import { reducerFactory } from 'topcoder-react-lib';
2223
import { getAuthTokens } from 'utils/tc';
@@ -113,6 +114,21 @@ function generateSsrOptions(req) {
113114
return res;
114115
}
115116

117+
/**
118+
* Generate user id hash for secure Identity verification
119+
* @param {Object} user
120+
* @return {String} User Id Hash.
121+
*/
122+
function generateUserIdHash(user) {
123+
const secret = _.get(config, 'SECRET.CHAMELEON_VERIFICATION_SECRET');
124+
const now = Math.floor(Date.now() / 1000);
125+
126+
return [
127+
crypto.createHmac('sha256', secret).update(`${user.userId}-${now}`).digest('hex'),
128+
now,
129+
].join('-');
130+
}
131+
116132
export function factory(req) {
117133
return redux.resolveReducers({
118134
standard: reducerFactory(req && generateSsrOptions(req)),
@@ -125,6 +141,12 @@ export function factory(req) {
125141
page: pageFactory(req),
126142
}).then(resolvedReducers => redux.combineReducers((state) => {
127143
const res = { ...state };
144+
145+
const user = _.get(res, 'auth.user');
146+
if (user && isomorphy.isServerSide()) {
147+
res.auth.userIdHash = generateUserIdHash(user);
148+
}
149+
128150
if (req) {
129151
res.domain = `${req.protocol}://${req.headers.host || req.hostname}`;
130152
res.subdomainCommunity = getCommunityId(req.subdomains);

0 commit comments

Comments
 (0)