Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c6a0644

Browse files
committedJun 12, 2018
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");

‎src/front/src/app/git-access-control/access-control.html

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,21 @@ <h2>Git Access Control</h2>
2727
<tr ng-repeat="item in tableConfig.github.items" ng-class-even="'footable-even'" ng-class-odd="'footable-odd'">
2828
<td class="col-lg-2">{{item.name}}</td>
2929
<td class="col-lg-2">
30-
<button class="btn btn-sm btn-success" ng-click="getSharableLink(item, 'github')">
30+
<button class="btn btn-sm btn-success" ng-hide="item.gettingLink" ng-click="getSharableLink(item, 'github')">
3131
<strong>Get Link</strong>
3232
</button>
33-
<div ng-if="item.showLink">
34-
<br>
35-
<p>{{item.accessLink}}</p>
33+
<button class="btn btn-sm btn-success disabled" ng-show="item.gettingLink">
34+
<i class="fa fa-spinner fa-spin"></i>
35+
</button>
36+
<p uib-alert class="alert-success" ng-repeat="alert in item.alerts" dismiss-on-timeout="1200" close="item.alerts=[]">{{alert}}</p>
37+
<p uib-alert class="alert-error" ng-repeat="alert in item.errors" dismiss-on-timeout="1200" close="item.errors=[]">{{alert}}</p>
38+
<div ng-if="item.showLink" class="input-group" ng-hide="item.gettingLink">
39+
<input type="text" class="form-control" ng-model="item.accessLink" readonly></input>
40+
<a class="input-group-addon" uib-tooltip="Copy URL to clipboard" clipboard supported="supported" text="item.accessLink"
41+
on-copied="item.alerts=[];item.alerts.push('Link has been copied to the clipboard')"
42+
on-error="item.errors=[];item.errors.push('An error occurred while copying link to clipboard')">
43+
<i class="fa fa-clipboard"></i>
44+
</a>
3645
</div>
3746
</td>
3847
</tr>
@@ -79,7 +88,7 @@ <h2>Git Access Control</h2>
7988
</a>
8089
</div>
8190
</uib-tab>
82-
<uib-tab index="1" heading="Gitlab" select="tabChanged('gitlab')">
91+
<uib-tab index="1" heading="Gitlab" select="tabChanged('gitlab')">
8392
<br/>
8493
<div ng-if="settings.gitlab==true">
8594
<div ng-show="tableConfig.gitlab.initialized">
@@ -94,12 +103,21 @@ <h2>Git Access Control</h2>
94103
<tr ng-repeat="item in tableConfig.gitlab.items" ng-class-even="'footable-even'" ng-class-odd="'footable-odd'">
95104
<td class="col-lg-2">{{item.name}}</td>
96105
<td class="col-lg-2">
97-
<button class="btn btn-sm btn-success" ng-click="getSharableLink(item, 'gitlab')">
106+
<button class="btn btn-sm btn-success" ng-hide="item.gettingLink" ng-click="getSharableLink(item, 'gitlab')">
98107
<strong>Get Link</strong>
99108
</button>
100-
<div ng-if="item.showLink">
101-
<br>
102-
<p>{{item.accessLink}}</p>
109+
<button class="btn btn-sm btn-success disabled" ng-show="item.gettingLink">
110+
<i class="fa fa-spinner fa-spin"></i>
111+
</button>
112+
<p uib-alert class="alert-success" ng-repeat="alert in item.alerts" dismiss-on-timeout="1200" close="item.alerts=[]">{{alert}}</p>
113+
<p uib-alert class="alert-error" ng-repeat="alert in item.errors" dismiss-on-timeout="1200" close="item.errors=[]">{{alert}}</p>
114+
<div ng-if="item.showLink" class="input-group" ng-hide="item.gettingLink">
115+
<input type="text" class="form-control" ng-model="item.accessLink" readonly></input>
116+
<a class="input-group-addon" uib-tooltip="Copy URL to clipboard" clipboard supported="supported" text="item.accessLink"
117+
on-copied="item.alerts=[];item.alerts.push('Link has been copied to the clipboard')"
118+
on-error="item.errors=[];item.errors.push('An error occurred while copying link to clipboard')">
119+
<i class="fa fa-clipboard"></i>
120+
</a>
103121
</div>
104122
</td>
105123
</tr>

‎src/front/src/app/git-access-control/gitAccessControl.controller.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ angular.module('topcoderX').controller('GitAccessController', ['currentUser', '$
1414
totalPages: 1,
1515
searchMethod: GitAccessControlService.getGithubOwnerTeams,
1616
initialized: false,
17+
accessLinkMethod: GitAccessControlService.getGithubShareableLink,
1718
},
1819
gitlab: {
1920
pageNumber: 1,
@@ -23,6 +24,7 @@ angular.module('topcoderX').controller('GitAccessController', ['currentUser', '$
2324
totalPages: 1,
2425
searchMethod: GitAccessControlService.getGitlabOwnerGroups,
2526
initialized: false,
27+
accessLinkMethod: GitAccessControlService.getGitlabShareableLink,
2628
}
2729
}
2830

@@ -52,9 +54,6 @@ angular.module('topcoderX').controller('GitAccessController', ['currentUser', '$
5254
if ($scope.settings.github) {
5355
_getOwnerList('github');
5456
}
55-
if ($scope.settings.gitlab) {
56-
_getOwnerList('gitlab');
57-
}
5857
$scope.isLoaded = true;
5958
}).catch(function (error) {
6059
$scope.isLoaded = true;
@@ -66,17 +65,16 @@ angular.module('topcoderX').controller('GitAccessController', ['currentUser', '$
6665
* getSharableLink Get the register url for a team
6766
*/
6867
$scope.getSharableLink = function (team, provider) {
69-
if (provider === 'github') {
70-
GitAccessControlService.getGithubShareableLink(team.id).then(function (response) {
71-
team.accessLink = response.data.url;
72-
team.showLink = true;
73-
}).catch(_handleError);
74-
} else {
75-
GitAccessControlService.getGitlabShareableLink(team.id).then(function (response) {
76-
team.accessLink = response.data.url;
77-
team.showLink = true;
78-
}).catch(_handleError);
79-
}
68+
team.gettingLink = true;
69+
var config = $scope.tableConfig[provider];
70+
config.accessLinkMethod.apply(vm, [team.id]).then(function (response) {
71+
team.accessLink = response.data.url;
72+
team.showLink = true;
73+
team.gettingLink = false;
74+
}).catch(function (err) {
75+
team.gettingLink = false;
76+
_handleError(err);
77+
});
8078
}
8179

8280
// handle errors

‎src/front/src/app/members/member.html

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@ <h2>{{title}}</h2>
66
</div>
77
</div>
88

9-
<div class="wrapper wrapper-content animated fadeInRight">
9+
<div class="wrapper gray-bg wrapper-content animated fadeInRight">
1010
<div class="row">
1111
<div class="col-lg-12">
12-
<div class="text-center m-t-lg" ng-if="provider==='github'">
13-
<h3>You were successfully invited to the team!</h3>
14-
<p>An invitation email will be sent to you if you are not already in the team.</p>
15-
</div>
16-
<div class="text-center m-t-lg" ng-if="provider==='gitlab'">
17-
<p>You were successfully added to the group!</p>
12+
<div class="ibox float-e-margins">
13+
<div class="ibox-content">
14+
<div class="text-center m-t-lg" ng-if="provider==='github'">
15+
<h3>You were successfully invited to the team!</h3>
16+
<p>An invitation email will be sent to you if you are not already in the team.</p>
17+
</div>
18+
<div class="text-center m-t-lg" ng-if="provider==='gitlab'">
19+
<p>You were successfully added to the group!</p>
20+
</div>
21+
</div>
1822
</div>
1923
</div>
2024
</div>

‎src/front/src/app/projects/project.service.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ angular.module('topcoderX')
2222
/**
2323
* Get all projects
2424
*/
25-
ProjectService.getProjects = function () {
26-
return $http.get(Helper.baseUrl + '/api/v1/projects').then(function (response) {
25+
ProjectService.getProjects = function (status) {
26+
return $http.get(Helper.baseUrl + '/api/v1/projects?status=' + status).then(function (response) {
2727
return response;
2828
});
2929
};

‎src/front/src/app/projects/projects.controller.js

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ angular.module('topcoderX')
3333
hideAll();
3434

3535
//private function to get projects.
36-
var getProjects = function () {
37-
ProjectService.getProjects().then(function (response) {
36+
$scope.getProjects = function (status) {
37+
$scope.isLoaded = false;
38+
$scope.projects = [];
39+
ProjectService.getProjects(status).then(function (response) {
3840
$scope.isLoaded = true;
39-
$scope.projects = $filter('filter')(response.data, { archived: false });
40-
$scope.archivedProjects = $filter('filter')(response.data, { archived: true });
41+
$scope.projects = response.data;
4142
$('.footable').trigger('footable_initialize');
4243
}).catch(function (error) {
4344
$scope.isLoaded = true;
@@ -48,21 +49,13 @@ angular.module('topcoderX')
4849
}
4950
});
5051
};
51-
getProjects();
52-
53-
//togole the archived table on the ui.
54-
$scope.togoArchiveTable = function () {
55-
$scope.archiveShow = !$scope.archiveShow;
56-
if ($scope.archiveShow) {
57-
$scope.init();
58-
}
59-
}
52+
$scope.getProjects('active');
6053

6154
$scope.repoType = function (repo) {
6255
return (repo.toLocaleLowerCase().indexOf("gitlab") >= 0 ? "Gitlab" : "Github");
63-
}
56+
};
6457

6558
$scope.init = function () {
6659
$('.footable').footable();
67-
}
60+
};
6861
}]);

‎src/front/src/app/projects/projects.html

Lines changed: 99 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -4,106 +4,112 @@
44
<div class="col-lg-6">
55
<h2>{{title}}</h2>
66
</div>
7-
<div class="col-lg-2 with-button">
8-
<button class="btn btn-sm btn-info" ng-click="goProject()">+</button>
7+
<div class="col-lg-2 with-button pull-right">
8+
<button class="btn btn-sm btn-info pull-right" ng-click="goProject()">
9+
<i class="fa fa-plus"></i> Add Project</button>
910
</div>
1011
</div>
1112

1213
<div class="wrapper wrapper-content animated fadeInRight">
13-
<div class="row" ng-if="isLoaded">
14+
<div class="row">
1415
<div class="col-lg-12">
1516
<div class="ibox float-e-margins">
1617
<div class="ibox-content">
17-
<div ng-show="projects.length===0">
18-
<div class="text-center m-t-lg">
19-
<h4>You don't have active projects right now. Please
20-
<button class="btn btn-sm btn-info" ng-click="goProject()">
21-
<strong>
22-
Add a Project
23-
</strong>
24-
</button>
25-
</h4>
26-
</div>
27-
</div>
28-
<div ng-hide="projects.length===0">
29-
<h3> Active Projects:</h3>
30-
<table class="footable table table-stripped toggle-arrow-tiny" data-page-navigation=".pagination" data-page-size="10" ng-init="init()">
31-
<thead>
32-
<tr>
33-
<th class="col-lg-2">Project Name</th>
34-
<th class="col-lg-2">Topcoder Direct ID</th>
35-
<th class="col-lg-2">Gitlab/Github Repo Url</th>
36-
<th class="col-lg-3" data-sort-ignore="true"></th>
37-
</tr>
38-
</thead>
39-
<tbody>
40-
<tr ng-repeat="project in projects" class="animate-repeat" ng-class-even="'footable-even'" ng-class-odd="'footable-odd'">
41-
<td class="col-lg-2">{{project.title}}</td>
42-
<td class="col-lg-2">
43-
<a href="{{directUrlBase}}{{project.tcDirectId}}" target="_blank">{{project.tcDirectId}}</a>
44-
</td>
45-
<td class="col-lg-2">
46-
<a href="{{project.repoUrl}}" target="_blank"> {{repoType(project.repoUrl)}} </a>
47-
</td>
48-
<td class="col-lg-2">
49-
<button class="btn btn-sm btn-success" ng-click="goProject(project)">
50-
<strong>Edit</strong>
51-
</button>
52-
</td>
53-
</tr>
54-
</tbody>
55-
<tfoot>
56-
<tr>
57-
<td colspan="4">
58-
<ul class="pagination pull-right"></ul>
59-
</td>
60-
</tr>
61-
</tfoot>
62-
</table>
63-
</div>
64-
65-
<div class="with-button">
66-
<button class="btn btn-sm btn-info" ng-click="togoArchiveTable()">
67-
<strong>
68-
Toggle Archived Projects
69-
</strong>
70-
</button>
71-
</div>
72-
73-
<div ng-show="archiveShow">
74-
<h3> Actieved Project:</h3>
75-
<table class="footable table table-stripped toggle-arrow-tiny" data-page-size="10">
76-
<thead>
77-
<tr>
78-
<th class="col-lg-2">Project Name</th>
79-
<th class="col-lg-2">Topcoder Direct ID</th>
80-
<th class="col-lg-2">Gitlab/Github Repo Url</th>
81-
<th class="col-lg-2" data-sort-ignore="true"></th>
82-
</tr>
83-
</thead>
84-
<tbody>
85-
<tr ng-repeat="archivedProject in archivedProjects">
86-
<td class="col-lg-2">{{archivedProject.title}}</td>
87-
<td class="col-lg-2">
88-
<a>{{archivedProject.tcDirectId}}</a>
89-
</td>
90-
<td class="col-lg-2">{{archivedProject.repoUrl}}</td>
91-
<td class="col-lg-2">
92-
<button class="btn btn-sm btn-success" ng-click="goProject(archivedProject)">
93-
<strong>Edit</strong>
94-
</button>
95-
</td>
96-
</tr>
97-
</tbody>
98-
<tfoot>
99-
<tr>
100-
<td colspan="4">
101-
<ul class="pagination pull-right"></ul>
102-
</td>
103-
</tr>
104-
</tfoot>
105-
</table>
106-
</div>
18+
<uib-tabset active="active">
19+
<uib-tab index="0" heading="Current Projects" select="getProjects('active')">
20+
<br/>
21+
<div ng-if="isLoaded">
22+
<div ng-show="projects.length===0">
23+
<div class="text-center m-t-lg">
24+
<h4>You don't have active projects right now. Please
25+
<button class="btn btn-sm btn-info" ng-click="goProject()">
26+
<strong>
27+
Add a Project
28+
</strong>
29+
</button>
30+
</h4>
31+
</div>
32+
</div>
33+
<div ng-hide="projects.length===0">
34+
<table class="footable table table-stripped toggle-arrow-tiny" data-page-navigation=".pagination" data-page-size="10"
35+
ng-init="init()">
36+
<thead>
37+
<tr>
38+
<th class="col-lg-2">Project Name</th>
39+
<th class="col-lg-2">Topcoder Direct ID</th>
40+
<th class="col-lg-2">Gitlab/Github Repo Url</th>
41+
<th class="col-lg-3" data-sort-ignore="true"></th>
42+
</tr>
43+
</thead>
44+
<tbody>
45+
<tr ng-repeat="project in projects" class="animate-repeat" ng-class-even="'footable-even'" ng-class-odd="'footable-odd'">
46+
<td class="col-lg-2">{{project.title}}</td>
47+
<td class="col-lg-2">
48+
<a href="{{directUrlBase}}{{project.tcDirectId}}" target="_blank">{{project.tcDirectId}}</a>
49+
</td>
50+
<td class="col-lg-2">
51+
<a href="{{project.repoUrl}}" target="_blank"> {{repoType(project.repoUrl)}} </a>
52+
</td>
53+
<td class="col-lg-2">
54+
<button class="btn btn-sm btn-success" ng-click="goProject(project)">
55+
<strong>Edit</strong>
56+
</button>
57+
</td>
58+
</tr>
59+
</tbody>
60+
<tfoot>
61+
<tr>
62+
<td colspan="4">
63+
<ul class="pagination pull-right"></ul>
64+
</td>
65+
</tr>
66+
</tfoot>
67+
</table>
68+
</div>
69+
</div>
70+
</uib-tab>
71+
<uib-tab index="1" heading="Archived Projects" select="getProjects('archived')">
72+
<br/>
73+
<div ng-if="isLoaded">
74+
<div ng-show="projects.length===0" class="text-center m-t-lg">
75+
<p>No projects have been archived.</p>
76+
</div>
77+
<div ng-hide="projects.length===0">
78+
<table class="footable table table-stripped toggle-arrow-tiny" data-page-size="10">
79+
<thead>
80+
<tr>
81+
<th class="col-lg-2">Project Name</th>
82+
<th class="col-lg-2">Topcoder Direct ID</th>
83+
<th class="col-lg-2">Gitlab/Github Repo Url</th>
84+
<th class="col-lg-2" data-sort-ignore="true"></th>
85+
</tr>
86+
</thead>
87+
<tbody>
88+
<tr ng-repeat="archivedProject in projects">
89+
<td class="col-lg-2">{{archivedProject.title}}</td>
90+
<td class="col-lg-2">
91+
<a>{{archivedProject.tcDirectId}}</a>
92+
</td>
93+
<td class="col-lg-2">{{archivedProject.repoUrl}}</td>
94+
<td class="col-lg-2">
95+
<button class="btn btn-sm btn-success" ng-click="goProject(archivedProject)">
96+
<strong>Edit</strong>
97+
</button>
98+
</td>
99+
</tr>
100+
</tbody>
101+
<tfoot>
102+
<tr>
103+
<td colspan="4">
104+
<ul class="pagination pull-right"></ul>
105+
</td>
106+
</tr>
107+
</tfoot>
108+
</table>
109+
</div>
110+
</div>
111+
</uib-tab>
112+
</uib-tabset>
107113
</div>
108114
</div>
109115
</div>

‎src/front/src/app/upsertproject/upsertproject.controller.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ angular.module('topcoderX').controller('ProjectController', ['currentUser', '$sc
1919
'title': '',
2020
'tcDirectId': '',
2121
'repoUrl': '',
22-
'rocketChatWebhook': '',
23-
'rocketChatChannelName': '',
22+
'rocketChatWebhook': null,
23+
'rocketChatChannelName': null,
2424
'archived': false
2525
};
2626
if ($rootScope.project) {

‎src/front/src/app/upsertproject/upsertproject.html

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@
44
<div class="col-lg-4">
55
<h2>{{title}}</h2>
66
</div>
7-
<div class="col-lg-2 with-button" ng-show="editing">
8-
<button class="btn btn-sm btn-info" ng-click="addLabels()">
9-
<strong>Add Labels</strong>
10-
</button>
11-
</div>
12-
<div class="col-lg-2 with-button" ng-show="editing">
13-
<button class="btn btn-sm btn-info" ng-click="addHooks()">
14-
<strong>
15-
Add Webhooks
16-
</strong>
17-
</button>
7+
<div class="pull-right">
8+
<div class="col-lg-6 with-button" ng-show="editing">
9+
<button class="btn btn-sm btn-info" ng-click="addLabels()">
10+
<strong>Add Labels</strong>
11+
</button>
12+
</div>
13+
<div class="col-lg-6 with-button" ng-show="editing">
14+
<button class="btn btn-sm btn-info" ng-click="addHooks()">
15+
<strong>
16+
Add Webhooks
17+
</strong>
18+
</button>
19+
</div>
1820
</div>
1921
</div>
2022

@@ -35,18 +37,21 @@ <h2>{{title}}</h2>
3537
<input class="form-control" type="url" ng-model="project.repoUrl" required />
3638
<span ng-show="projectForm.project.repoUrl.$touched && projectForm.project.repoUrl.$invalid">The TC Repo URL is required.</span>
3739
<br />
38-
<label class="form-label">Rocket.chat Webhook URL:</label>
39-
<input class="form-control" type="url" ng-model="project.rocketChatWebhook" required/>
40-
<span ng-show="projectForm.project.rocketChatWebhook.$touched && projectForm.project.rocketChatWebhook.$invalid">The Rocket Chat Webhook is required.</span>
41-
<br />
42-
<label class="form-label">Rocket.chat Channel Name:</label>
43-
<input class="form-control" type="text" ng-model="project.rocketChatChannelName" required/>
44-
<span ng-show="projectForm.project.rocketChatChannelName.$touched && projectForm.project.rocketChatChannelName.$invalid">The Rocket Chat Channel Name is required.</span>
45-
<br />
40+
<div class="hide">
41+
<label class="form-label">Rocket.chat Webhook URL:</label>
42+
<input class="form-control" type="url" ng-model="project.rocketChatWebhook"/>
43+
<span ng-show="projectForm.project.rocketChatWebhook.$touched && projectForm.project.rocketChatWebhook.$invalid">The Rocket Chat Webhook is required.</span>
44+
<br />
45+
<label class="form-label">Rocket.chat Channel Name:</label>
46+
<input class="form-control" type="text" ng-model="project.rocketChatChannelName"/>
47+
<span ng-show="projectForm.project.rocketChatChannelName.$touched && projectForm.project.rocketChatChannelName.$invalid">The Rocket Chat Channel Name is required.</span>
48+
<br />
49+
</div>
4650
<label class="form-label">Archived:</label>
4751
<input class="checkbox-control" type="checkbox" ng-model="project.archived" ng-checked="project.archived === 'true'"
4852
ng-model="project.archived" ng-true-value="'true'" ng-false-value="'false'" />
4953
<br/>
54+
5055
<button class="with-button btn btn-sm btn-info" ng-click="projectForm.$valid && save()">
5156
<strong>
5257
Save

‎src/front/src/components/alert/alert.controller.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ angular.module('topcoderX')
99
};
1010

1111
$scope.addAlert = function (alert) {
12+
$scope.alert = [];
1213
$scope.alerts.push(alert);
1314
};
1415

‎src/front/src/components/alert/alert.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ angular.module('topcoderX')
1818
};
1919

2020
Alert.issue = function (alert, $scope) {
21+
Alert.clear($scope);
2122
$log.debug('Alert#alert::type:' + alert.type + ',message:' + alert.message);
2223
var scope = $scope || $rootScope
2324
scope.$broadcast('alert.AlertIssued', alert);

‎src/front/src/components/common/navigation.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
</a>
2727
</li>
2828

29-
<li ui-sref-active="active">
29+
<li ui-sref-active="active" class="hide">
3030
<a href="#">
3131
<i class="fa fa-code"></i>
3232
<span class="nav-label">My Challenges</span>
@@ -41,7 +41,7 @@
4141
</li>
4242
</ul>
4343
</li>
44-
<li ui-sref-active="active">
44+
<li ui-sref-active="active" class="hide">
4545
<a ui-sref="app.changelog">
4646
<i class="fa fa-history"></i>
4747
<span class="nav-label">Changelog</span>

‎src/models/Project.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ const schema = new mongoose.Schema({
1616
title: { type: String, required: true },
1717
tcDirectId: { type: Number, required: true },
1818
repoUrl: { type: String, required: true },
19-
rocketChatWebhook: { type: String, required: true },
20-
rocketChatChannelName: { type: String, required: true },
19+
rocketChatWebhook: { type: String, required: false },
20+
rocketChatChannelName: { type: String, required: false },
2121
archived: { type: String, required: true },
2222
username: { type: String, required: true },
2323
secretWebhookKey: { type: String, required: true },

‎src/routes.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ module.exports = {
3838
get: {
3939
controller: 'GithubController',
4040
method: 'addUserToTeam',
41+
allowNormalUser: true,
4142
tcLogin: true,
4243
},
4344
},
4445
'/github/normaluser/callback': {
4546
get: {
4647
controller: 'GithubController',
4748
method: 'addUserToTeamCallback',
49+
allowNormalUser: true,
4850
},
4951
},
5052

@@ -77,13 +79,15 @@ module.exports = {
7779
get: {
7880
controller: 'GitlabController',
7981
method: 'addUserToGroup',
82+
allowNormalUser: true,
8083
tcLogin: true,
8184
},
8285
},
8386
'/gitlab/normaluser/callback': {
8487
get: {
8588
controller: 'GitlabController',
8689
method: 'addUserToGroupCallback',
90+
allowNormalUser: true,
8791
},
8892
},
8993

@@ -97,7 +101,6 @@ module.exports = {
97101
get: {
98102
controller: 'TCUserController',
99103
method: 'getUserMapping',
100-
isAdmin: true,
101104
},
102105
},
103106
'/projects': {
@@ -138,4 +141,11 @@ module.exports = {
138141
method: 'getUserToken',
139142
},
140143
},
144+
'/security/isAuthorized': {
145+
get: {
146+
controller: 'SecurityController',
147+
method: 'isAuthorized',
148+
allowNormalUser: true,
149+
},
150+
},
141151
};

‎src/services/ProjectService.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ const projectSchema = {
2424
title: Joi.string().required(),
2525
tcDirectId: Joi.number().required(),
2626
repoUrl: Joi.string().required(),
27-
rocketChatWebhook: Joi.string().required(),
28-
rocketChatChannelName: Joi.string().required(),
27+
rocketChatWebhook: Joi.string().allow(null),
28+
rocketChatChannelName: Joi.string().allow(null),
2929
archived: Joi.boolean().required(),
3030
username: Joi.string().required(),
31-
secretWebhookKey: Joi.string().required()
31+
secretWebhookKey: Joi.string().required(),
3232
}
3333
};
3434

@@ -37,8 +37,8 @@ const createProjectSchema = {
3737
title: Joi.string().required(),
3838
tcDirectId: Joi.number().required(),
3939
repoUrl: Joi.string().required(),
40-
rocketChatWebhook: Joi.string().required(),
41-
rocketChatChannelName: Joi.string().required(),
40+
rocketChatWebhook: Joi.string().allow(null),
41+
rocketChatChannelName: Joi.string().allow(null),
4242
archived: Joi.boolean().required(),
4343
username: Joi.string().required(),
4444
}
@@ -102,12 +102,20 @@ update.schema = projectSchema;
102102

103103
/**
104104
* gets all projects
105+
* @param {String} status the status of project
105106
* @returns {Array} all projects
106107
*/
107-
async function getAll() {
108-
return await Project.find({});
108+
async function getAll(status) {
109+
if (status === 'archived') {
110+
return await Project.find({ archived: true });
111+
}
112+
return await Project.find({ archived: false });
109113
}
110114

115+
getAll.schema = Joi.object().keys({
116+
status: Joi.string().required().allow('active', 'archived').default('active'),
117+
});
118+
111119
/**
112120
* creates label
113121
* @param {Object} body the request body

‎src/services/SecurityService.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2018 TopCoder, Inc. All rights reserved.
3+
*/
4+
5+
/**
6+
* This service will provide security related operations.
7+
*
8+
* @author veshu
9+
* @version 1.0
10+
*/
11+
const Joi = require('joi');
12+
const _ = require('lodash');
13+
const config = require('../config');
14+
const helper = require('../common/helper');
15+
16+
17+
/**
18+
* check if given roles are authorized for this app
19+
* @param {Array} roles the roles to check
20+
* @returns {Promise} the promise result whether roles are allowed or not
21+
*/
22+
async function isRolesAllowed(roles) {
23+
// at least one of the parameters should ge provided
24+
return _(roles).map((i) => i.toLowerCase())
25+
.intersection(_.map(config.ALLOWED_TOPCODER_ROLES, (j) => j.toLowerCase())).size() > 0;
26+
}
27+
28+
isRolesAllowed.schema = {
29+
roles: Joi.array().items(Joi.string()).required(),
30+
};
31+
32+
33+
module.exports = {
34+
isRolesAllowed,
35+
};
36+
37+
helper.buildService(module.exports);

0 commit comments

Comments
 (0)
This repository has been archived.