Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit c6a0644

Browse files
committed
UI updates and better handling of close events.
1 parent 84216a6 commit c6a0644

28 files changed

+377
-229
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,6 @@ Note: heroku domain should match subdomain of topcoder-dev or topcoder depending
7979
- setup both git provider to authorize topcoder-x to manage your repo on behalf of you
8080
- go to project management and create/edit projects, create hook and label
8181
- go to git access control menu and check list of groups have authorized
82-
- click get link button to get the shareable link which can be used by topcoder member to self assign to the repository.
82+
- click get link button to get the shareable link which can be used by topcoder member to self assign to the repository. Click to icon next to url to copy to clipboard.
83+
- normal member cannot use the application, allowed roles are configured in API, if normal user tries to access the app, error is shown in login page.
8384

src/.DS_Store

0 Bytes
Binary file not shown.

src/.eslintrc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends":"eslint-config-topcoder/nodejs",
2+
"extends": "eslint-config-topcoder/nodejs",
33
"env": {
44
"mocha": true
55
},
@@ -10,5 +10,7 @@
1010
"experimentalObjectRestSpread": true
1111
}
1212
},
13-
"rules": { }
13+
"rules": {
14+
"lodash/prefer-invoke-map": 0
15+
}
1416
}

src/app.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ _.forEach(routes, (verbs, path) => {
4444
const decoded = jwtDecode(v3jwt);
4545
req.currentUser = {
4646
handle: decoded.handle.toLowerCase(),
47+
roles: decoded.roles,
4748
};
4849
}
4950
req.signature = `${def.controller}#${def.method}`;
@@ -61,6 +62,20 @@ _.forEach(routes, (verbs, path) => {
6162
return res.redirect(`${constants.TOPCODER_VALUES[config.TOPCODER_ENV].TC_LOGIN_URL}?retUrl=${encodeURIComponent(callbackUri)}`);
6263
});
6364
}
65+
if (!def.allowNormalUser) {
66+
actions.push((req, res, next) => {
67+
// check if any allowed role is matched with user's roles
68+
if (_(req.currentUser.roles).map((i) => i.toLowerCase())
69+
.intersection(_.map(config.ALLOWED_TOPCODER_ROLES, (j) => j.toLowerCase())).size() === 0) {
70+
const statusCode = 403;
71+
return res.status(statusCode).json({
72+
code: 'Forbidden',
73+
message: 'You are not allowed to access this resource.',
74+
});
75+
}
76+
return next();
77+
});
78+
}
6479
actions.push(method);
6580
app[verb](`/api/${config.API_VERSION}${path}`, actions);
6681
});

src/common/constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const GITHUB_OWNER_CALLBACK_URL = '/api/v1/github/owneruser/callback';
6363
const GITLAB_OWNER_CALLBACK_URL = '/api/v1/gitlab/owneruser/callback';
6464

6565
const OWNER_USER_LOGIN_SUCCESS_URL = '/#/app/settings';
66-
const USER_ADDED_TO_TEAM_SUCCESS_URL = '/#/app/members';
66+
const USER_ADDED_TO_TEAM_SUCCESS_URL = '/#/members';
6767

6868
const TC_LOGIN_CALLBACK_URL = '/api/v1/tclogin';
6969
const JWT_V3_NAME = 'v3jwt';

src/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ module.exports = {
3434
},
3535
HOOK_BASE_URL: process.env.HOOK_BASE_URL || 'http://x.topcoder-dev.com/',
3636
TOPCODER_ENV: process.env.TOPCODER_ENV || 'dev',
37-
LABELS: process.env.LABELS || [{ name: 'Open for pickup', color: '112233' }, { name: 'Assigned', color: '445566' }, { name: 'Ready for review', color: '123123' }, { name: 'Paid', color: '456456' }, { name: 'Feedback', color: 'ff0011' }, { name: 'Fix accepted', color: 'aabb11' },]
37+
LABELS: process.env.LABELS || [{ name: 'Open for pickup', color: '112233' }, { name: 'Assigned', color: '445566' }, { name: 'Ready for review', color: '123123' }, { name: 'Paid', color: '456456' }, { name: 'Feedback', color: 'ff0011' }, { name: 'Fix accepted', color: 'aabb11' }],
38+
ALLOWED_TOPCODER_ROLES: process.env.ALLOWED_TOPCODER_ROLES || ['administrator', 'admin', 'connect manager', 'connect admin'],
3839
};

src/controllers/ProjectController.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,10 @@ async function update(req) {
3434
/**
3535
* get all projects
3636
* @param {Object} req the request
37-
* @param {Object} res the response
3837
* @returns {Array} the result
3938
*/
40-
async function getAll() {
41-
return await ProjectService.getAll();
39+
async function getAll(req) {
40+
return await ProjectService.getAll(req.query.status);
4241
}
4342

4443
/**

src/controllers/SecurityController.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2018 TopCoder, Inc. All rights reserved.
3+
*/
4+
5+
/**
6+
* This controller exposes security related endpoints.
7+
*
8+
* @author veshu
9+
* @version 1.0
10+
*/
11+
12+
const helper = require('../common/helper');
13+
const securityService = require('../services/SecurityService');
14+
15+
/**
16+
* check if current user is authorized for Topcoder X or not.
17+
* @param {Object} req the request
18+
* @returns {Object} the result
19+
*/
20+
async function isAuthorized(req) {
21+
return await securityService.isRolesAllowed(req.currentUser.roles);
22+
}
23+
24+
module.exports = {
25+
isAuthorized,
26+
};
27+
28+
helper.buildController(module.exports);

src/front/config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"local":{"API_URL":"https://127.0.0.1:8443","ADMIN_TOOL_URL":"http://localhost:8080/api/v2","API_VERSION_PATH":"v3","COOKIES_SECURE":false,"AUTH_URL":"https://accounts.topcoder-dev.com/member","ACCOUNTS_CONNECTOR_URL":"https://accounts.topcoder-dev.com/connector.html","JWT_V3_NAME":"v3jwt","JWT_V2_NAME":"tcjwt","BACKEND_API":"http://localhost:4000","DIRECT_URL_BASE":"https://www.topcoder-dev.com/direct/projectOverview?formData.projectId=","LABELS":["Open for pickup","Assigned","Ready for review","Paid","Feedback","Fix accepted"],"LABELS_COLOR":["112233","445566","123123","456456","ff0011","aabb11"],"HOOK_BASE_URL":"https://b9602b91.ngrok.io","OWNER_LOGIN_GITHUB_URL":"/api/v1/github/owneruser/login","OWNER_LOGIN_GITLAB_URL":"/api/v1/gitlab/owneruser/login"},"heroku":{"API_URL":"https://api.topcoder-dev.com","ADMIN_TOOL_URL":"https://api.topcoder-dev.com/v2","API_VERSION_PATH":"v3","COOKIES_SECURE":false,"AUTH_URL":"https://accounts.topcoder-dev.com/member","ACCOUNTS_CONNECTOR_URL":"https://accounts.topcoder-dev.com/connector.html","JWT_V3_NAME":"v3jwt","JWT_V2_NAME":"tcjwt","BACKEND_API":"http://topcoder-x-backend-dev.herokuapp.com","DIRECT_URL_BASE":"https://www.topcoder-dev.com/direct/projectOverview?formData.projectId=","LABELS":["Open for pickup","Assigned","Ready for review","Paid","Feedback","Fix accepted"],"LABELS_COLOR":["112233","445566","123123","456456","ff0011","aabb11"],"HOOK_BASE_URL":"http://x.topcoder-dev.com/","OWNER_LOGIN_GITHUB_URL":"/api/v1/github/owneruser/login","OWNER_LOGIN_GITLAB_URL":"/api/v1/gitlab/owneruser/login"},"dev":{"API_URL":"https://api.topcoder-dev.com","ADMIN_TOOL_URL":"https://api.topcoder-dev.com/v2","API_VERSION_PATH":"v3","COOKIES_SECURE":false,"AUTH_URL":"https://accounts.topcoder-dev.com/member","ACCOUNTS_CONNECTOR_URL":"https://accounts.topcoder-dev.com/connector.html","JWT_V3_NAME":"v3jwt","JWT_V2_NAME":"tcjwt","DIRECT_URL_BASE":"https://www.topcoder-dev.com/direct/projectOverview?formData.projectId=","LABELS":["Open for pickup","Assigned","Ready for review","Paid","Feedback","Fix accepted"],"LABELS_COLOR":["112233","445566","123123","456456","ff0011","aabb11"],"HOOK_BASE_URL":"https://b9602b91.ngrok.io","OWNER_LOGIN_GITHUB_URL":"/api/v1/github/owneruser/login","OWNER_LOGIN_GITLAB_URL":"/api/v1/gitlab/owneruser/login"},"qa":{"API_URL":"https://api.topcoder-qa.com","ADMIN_TOOL_URL":"https://api.topcoder-qa.com/v2","API_VERSION_PATH":"v3","COOKIES_SECURE":false,"AUTH_URL":"https://accounts.topcoder-qa.com/member","ACCOUNTS_CONNECTOR_URL":"https://accounts.topcoder-qa.com/connector.html","JWT_V3_NAME":"v3jwt","JWT_V2_NAME":"tcjwt","BACKEND_API":"http://localhost:4000","DIRECT_URL_BASE":"https://www.topcoder-dev.com/direct/projectOverview?formData.projectId=","LABELS":["Open for pickup","Assigned","Ready for review","Paid","Feedback","Fix accepted"],"LABELS_COLOR":["112233","445566","123123","456456","ff0011","aabb11"],"HOOK_BASE_URL":"https://b9602b91.ngrok.io","OWNER_LOGIN_GITHUB_URL":"/api/v1/github/owneruser/login","OWNER_LOGIN_GITLAB_URL":"/api/v1/gitlab/owneruser/login"},"prod":{"API_URL":"https://api.topcoder.com","ADMIN_TOOL_URL":"https://api.topcoder.com/v2","API_VERSION_PATH":"v3","COOKIES_SECURE":false,"AUTH_URL":"https://accounts.topcoder.com/member","ACCOUNTS_CONNECTOR_URL":"https://accounts.topcoder.com/connector.html","JWT_V3_NAME":"v3jwt","JWT_V2_NAME":"tcjwt","BACKEND_API":"http://topcoderx.topcoder.com:80/api/v1","DIRECT_URL_BASE":"https://www.topcoder-dev.com/direct/projectOverview?formData.projectId=","LABELS":["Open for pickup","Assigned","Ready for review","Paid","Feedback","Fix accepted"],"LABELS_COLOR":["112233","445566","123123","456456","ff0011","aabb11"],"HOOK_BASE_URL":"https://b9602b91.ngrok.io","OWNER_LOGIN_GITHUB_URL":"/api/v1/github/owneruser/login","OWNER_LOGIN_GITLAB_URL":"/api/v1/gitlab/owneruser/login"}}

src/front/src/app/app.js

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,24 +86,26 @@ angular.module('topcoderX', [
8686
data: { pageTitle: 'Project Management' },
8787
resolve: { auth: authenticate }
8888
})
89-
.state('app.challenges', {
90-
url: '/challenges',
91-
templateUrl: 'app/challenges/challenges.html',
92-
data: { pageTitle: 'Topcoder Platform' },
93-
resolve: { auth: authenticate }
94-
})
95-
.state('app.tickets', {
96-
url: '/tickets',
97-
templateUrl: 'app/challenges/tickets.html',
98-
data: { pageTitle: 'Git Tickets' },
99-
resolve: { auth: authenticate }
100-
})
101-
.state('app.changelog', {
102-
url: '/changelog',
103-
templateUrl: 'app/changelog/changelog.html',
104-
data: { pageTitle: 'Changelog' },
105-
resolve: { auth: authenticate }
106-
})
89+
// following code is commented to hide the menu
90+
// un comment this when pages are developed
91+
// .state('app.challenges', {
92+
// url: '/challenges',
93+
// templateUrl: 'app/challenges/challenges.html',
94+
// data: { pageTitle: 'Topcoder Platform' },
95+
// resolve: { auth: authenticate }
96+
// })
97+
// .state('app.tickets', {
98+
// url: '/tickets',
99+
// templateUrl: 'app/challenges/tickets.html',
100+
// data: { pageTitle: 'Git Tickets' },
101+
// resolve: { auth: authenticate }
102+
// })
103+
// .state('app.changelog', {
104+
// url: '/changelog',
105+
// templateUrl: 'app/changelog/changelog.html',
106+
// data: { pageTitle: 'Changelog' },
107+
// resolve: { auth: authenticate }
108+
// })
107109
.state('app.settings', {
108110
url: '/settings',
109111
templateUrl: 'app/settings/settings.html',
@@ -117,8 +119,9 @@ angular.module('topcoderX', [
117119
templateUrl: 'app/git-access-control/access-control.html',
118120
controller: 'GitAccessController',
119121
controllerAs: 'vm',
122+
resolve: { auth: authenticate }
120123
})
121-
.state('app.membersAdded', {
124+
.state('membersAdded', {
122125
url: '/members/:provider',
123126
templateUrl: 'app/members/member.html',
124127
controller: 'MemberController',

src/front/src/app/auth/auth.controller.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ angular.module('topcoderX')
1414

1515
// if we come to this page and user is logged in, and we are not loggin out
1616
// then we shouldn't be on this page so we redirect to index
17-
if (AuthService.isLoggedIn() && !AuthService.logginOut) {
17+
if (AuthService.isLoggedIn() && !AuthService.logginOut && !AuthService.PermissionDenied) {
1818
$state.go('app.main');
1919

2020
// if we are loggin out currently, then show "loggin out..." message

src/front/src/app/auth/auth.service.js

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
angular.module('topcoderX')
44
.factory('AuthService', [
5-
'$q', '$log', 'jwtHelper', '$cookies', '$window', '$state', '$rootScope',
5+
'$q', '$log', 'jwtHelper', '$cookies', '$window', '$state', '$rootScope', '$http',
66
'COOKIES_SECURE', 'JWT_V3_NAME', 'JWT_V2_NAME', 'Helper',
7-
function ($q, $log, jwtHelper, $cookies, $window, $state, $rootScope,
7+
function ($q, $log, jwtHelper, $cookies, $window, $state, $rootScope, $http,
88
COOKIES_SECURE, JWT_V3_NAME, JWT_V2_NAME, Helper) {
99
// these constants are for AuthService internal usage only
1010
// they don't depend on the environment thus don't have to be placed in global config
@@ -108,8 +108,9 @@ angular.module('topcoderX')
108108

109109
var AuthService = {
110110
ERROR: {
111-
NO_PERMISSIONS: 'Current user doesn\'t have administrator permissions.'
112-
}
111+
NO_PERMISSIONS: 'Current user doesn\'t have permissions.',
112+
},
113+
PermissionDenied: false,
113114
};
114115

115116
/**
@@ -134,13 +135,8 @@ angular.module('topcoderX')
134135
AuthService.retriveFreshToken = function () {
135136
return proxyCall(GET_FRESH_TOKEN_REQUEST, GET_FRESH_TOKEN_SUCCESS, GET_FRESH_TOKEN_FAILURE)
136137
.then(function (data) {
137-
var user = jwtHelper.decodeToken(data.token);
138-
139-
if ($.inArray('administrator', user && user.roles) < 0) {
140-
return $q.reject(AuthService.ERROR.NO_PERMISSIONS);
141-
} else {
142-
AuthService.setTokenV3(data.token);
143-
}
138+
AuthService.setTokenV3(data.token);
139+
return AuthService.isAuthorized();
144140
});
145141
}
146142

@@ -186,25 +182,31 @@ angular.module('topcoderX')
186182
* @return {Promise} promise to authenticate
187183
*/
188184
AuthService.authenticate = function () {
189-
return AuthService.ready().then(function () {
190-
if (AuthService.isLoggedIn()) {
191-
return $q.resolve();
185+
return AuthService.ready().then(function () {
186+
if (AuthService.isLoggedIn()) {
187+
return AuthService.isAuthorized();
188+
} else {
189+
if (AuthService.getTokenV2()) {
190+
return AuthService.retriveFreshToken();
192191
} else {
193-
if (AuthService.getTokenV2()) {
194-
return AuthService.retriveFreshToken().catch(function (err) {
195-
// if error about permission denied we will pass this error through
196-
// otherwise got to login page
197-
if (err !== AuthService.ERROR.NO_PERMISSIONS) {
198-
AuthService.login();
199-
}
200-
return $q.reject(err);
201-
});
202-
} else {
203-
AuthService.login();
204-
return $q.reject();
205-
}
192+
AuthService.login();
193+
return $q.reject();
206194
}
207-
});
195+
}
196+
});
197+
}
198+
199+
/**
200+
* checks if current user is allowed to use the app or not
201+
*/
202+
AuthService.isAuthorized = function () {
203+
return $http.get(Helper.baseUrl + '/api/v1/security/isAuthorized').then(function (res) {
204+
if (res.data === true) {
205+
return $q.resolve();
206+
}
207+
AuthService.PermissionDenied = true;
208+
return $q.reject(AuthService.ERROR.NO_PERMISSIONS);
209+
}).catch(function (err) { return $q.reject(err); });
208210
}
209211

210212
/**

src/front/src/app/config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
angular.module('app.constants', [])
2+
.constant('API_URL', "https://api.topcoder-dev.com")
3+
.constant('ADMIN_TOOL_URL', "https://api.topcoder-dev.com/v2")
4+
.constant('API_VERSION_PATH', "v3")
5+
.constant('COOKIES_SECURE', false)
6+
.constant('AUTH_URL', "https://accounts.topcoder-dev.com/member")
7+
.constant('ACCOUNTS_CONNECTOR_URL', "https://accounts.topcoder-dev.com/connector.html")
8+
.constant('JWT_V3_NAME', "v3jwt")
9+
.constant('JWT_V2_NAME', "tcjwt")
10+
.constant('DIRECT_URL_BASE', "https://www.topcoder-dev.com/direct/projectOverview?formData.projectId=")
11+
.constant('LABELS', ["Open for pickup","Assigned","Ready for review","Paid","Feedback","Fix accepted"])
12+
.constant('LABELS_COLOR', ["112233","445566","123123","456456","ff0011","aabb11"])
13+
.constant('HOOK_BASE_URL', "https://b9602b91.ngrok.io")
14+
.constant('OWNER_LOGIN_GITHUB_URL', "/api/v1/github/owneruser/login")
15+
.constant('OWNER_LOGIN_GITLAB_URL', "/api/v1/gitlab/owneruser/login");

0 commit comments

Comments
 (0)