Skip to content

Commit 058554e

Browse files
vikrantwiz02jkuesterclaude
authored
fix(medic#10341): revoke permissions when user role is deleted from app_settings (medic#10795)
Co-authored-by: Joshua Kuestersteffen <[email protected]> Co-authored-by: Claude Sonnet 4.6 <[email protected]>
1 parent 23225a5 commit 058554e

38 files changed

Lines changed: 949 additions & 915 deletions

File tree

admin/src/js/controllers/edit-user.js

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const moment = require('moment');
22
const passwordTester = require('simple-password-tester');
33
const phoneNumber = require('@medic/phone-number');
4-
const CHT = require('@medic/cht-datasource');
54
const constants = require('@medic/constants');
65
const USER_ROLES = constants.USER_ROLES;
76
const PASSWORD_MINIMUM_LENGTH = 8;
@@ -40,9 +39,8 @@ angular
4039
'use strict';
4140
'ngInject';
4241

43-
const datasource = DataContext.getDatasource();
42+
const datasourcePromise = DataContext.then(dataContext => dataContext.getDatasource());
4443
$scope.cancel = () => $uibModalInstance.dismiss();
45-
const getContact = DataContext.bind(CHT.Contact.v1.get);
4644

4745
const getRoles = roles => {
4846
if (!roles || !roles.length) {
@@ -61,9 +59,10 @@ angular
6159
});
6260
};
6361

64-
const validateSkipPasswordPermission = () => {
62+
const validateSkipPasswordPermission = datasource => {
6563
$scope.skipPasswordChange = datasource.v1.hasPermissions(
66-
['can_skip_password_change'], $scope.editUserModel.roles, $scope.permissions
64+
['can_skip_password_change'],
65+
$scope.editUserModel.roles
6766
);
6867
};
6968

@@ -112,7 +111,7 @@ angular
112111
// If $scope.model === {}, we're creating a new user.
113112
return $q.all([Settings(), getOidcUsername()])
114113
.then(([settings, oidcUsername]) => {
115-
$scope.permissions = settings.permissions;
114+
$scope.settings = settings;
116115
$scope.roles = settings.roles;
117116
$scope.allowTokenLogin = allowTokenLogin(settings);
118117
$scope.allowSSOLogin = allowSSOLogin(settings);
@@ -177,10 +176,10 @@ angular
177176
.map((row) => row.doc);
178177
};
179178

180-
this.setupPromise = determineEditUserModel()
181-
.then(model => {
179+
this.setupPromise = $q.all([determineEditUserModel(), datasourcePromise])
180+
.then(([model, datasource]) => {
182181
$scope.editUserModel = model;
183-
validateSkipPasswordPermission();
182+
validateSkipPasswordPermission(datasource);
184183
populateFacilitynContact();
185184
})
186185
.catch(err => {
@@ -324,13 +323,14 @@ angular
324323
return true;
325324
};
326325

327-
const validatePlacesPermission = () => {
326+
const validatePlacesPermission = datasource => {
328327
if (!$scope.editUserModel.place || $scope.editUserModel.place.length <= 1) {
329328
return true;
330329
}
331330

332331
const userHasPermission = datasource.v1.hasPermissions(
333-
['can_have_multiple_places'], $scope.editUserModel.roles, $scope.permissions
332+
['can_have_multiple_places'],
333+
$scope.editUserModel.roles
334334
);
335335

336336
if (!userHasPermission) {
@@ -388,16 +388,16 @@ angular
388388
});
389389
};
390390

391-
const validateContactIsInPlace = () => {
391+
const validateContactIsInPlace = datasource => {
392392
const placeIds = $scope.editUserModel.place;
393393
const contactId = $scope.editUserModel.contact;
394394
if (!placeIds || !contactId) {
395395
return $q.resolve(true);
396396
}
397397

398-
const getParent = (contactId) => {
399-
return getContact(CHT.Qualifier.byUuid(contactId)).then(contact => contact.parent);
400-
};
398+
const getParent = contactId => datasource.v1.contact
399+
.getByUuid(contactId)
400+
.then(contact => contact.parent);
401401

402402
const checkParent = (parent, placeIds) => {
403403
if (!parent) {
@@ -584,41 +584,44 @@ angular
584584
'password' ? 'text' : 'password';
585585
};
586586

587-
// #edit-user-profile is the admin view, which has additional fields.
588-
$scope.editUser = () => {
589-
$scope.setProcessing();
590-
$scope.errors = {};
591-
computeFields();
592-
587+
const runValidations = datasource => {
593588
const synchronousValidations = validateName() &&
594589
validateRole() &&
595590
validateContactAndFacility() &&
596591
validatePasswordForEditUser() &&
597592
validateEmailAddress() &&
598-
validatePlacesPermission();
593+
validatePlacesPermission(datasource);
599594

600595
if (!synchronousValidations) {
601596
$scope.setError();
602597
return;
603598
}
604599

605-
const asynchronousValidations = $q
600+
return $q
606601
.all([
607602
validateFacilityHierarchy(),
608-
validateContactIsInPlace(),
603+
validateContactIsInPlace(datasource),
609604
validateTokenLogin(),
610605
])
611-
.then(responses => responses.every(response => response));
612-
613-
return asynchronousValidations
606+
.then(responses => responses.every(Boolean))
614607
.then(valid => {
615608
if (!valid) {
616609
$scope.setError();
617610
return;
618611
}
619612

620613
return validateReplicationLimit().then(() => updateUser());
621-
})
614+
});
615+
};
616+
617+
// #edit-user-profile is the admin view, which has additional fields.
618+
$scope.editUser = () => {
619+
$scope.setProcessing();
620+
$scope.errors = {};
621+
computeFields();
622+
623+
return datasourcePromise
624+
.then(runValidations)
622625
.catch(err => {
623626
if (err.key) {
624627
$translate(err.key, err.params).then(value => $scope.setError(err, value, err.severity));

admin/src/js/services/auth.js

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ angular.module('inboxServices').factory('Auth',
22
function(
33
$log,
44
DataContext,
5-
Session,
6-
Settings
5+
Session
76
) {
87

98
'use strict';
109
'ngInject';
1110

12-
const datasource = DataContext.getDatasource();
13-
1411
/**
1512
* Receives a list of groups of permissions and returns a promise that will be resolved if the
1613
* current user's role has all the permissions of any of the provided groups.
@@ -23,21 +20,16 @@ angular.module('inboxServices').factory('Auth',
2320
return has(permissionsGroupList);
2421
}
2522

26-
return Settings()
27-
.then(settings => {
28-
if (!settings) {
29-
$log.debug('AuthService :: CHT-Core settings not configured.');
30-
return false;
31-
}
32-
23+
return DataContext
24+
.then(dataContext => {
3325
const userCtx = Session.userCtx();
3426

3527
if (!userCtx) {
3628
$log.debug('AuthService :: Not logged in.');
3729
return false;
3830
}
3931

40-
return datasource.v1.hasAnyPermission(permissionsGroupList, userCtx.roles, settings.permissions);
32+
return dataContext.getDatasource().v1.hasAnyPermission(permissionsGroupList, userCtx.roles);
4133
})
4234
.catch(() => false);
4335
};
@@ -49,21 +41,16 @@ angular.module('inboxServices').factory('Auth',
4941
* @param permissions {string | string[]}
5042
*/
5143
const has = (permissions) => {
52-
return Settings()
53-
.then(settings => {
54-
if (!settings) {
55-
$log.debug('AuthService :: CHT-Core settings not configured.');
56-
return false;
57-
}
58-
44+
return DataContext
45+
.then(dataContext => {
5946
const userCtx = Session.userCtx();
6047

6148
if (!userCtx) {
6249
$log.debug('AuthService :: Not logged in.');
6350
return false;
6451
}
6552

66-
return datasource.v1.hasPermissions(permissions, userCtx.roles, settings.permissions);
53+
return dataContext.getDatasource().v1.hasPermissions(permissions, userCtx.roles);
6754
})
6855
.catch(() => false);
6956
};
Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
const cht = require('@medic/cht-datasource');
2+
const constants = require('@medic/constants');
3+
const DOC_IDS = constants.DOC_IDS;
24

35
angular.module('inboxServices').factory('DataContext', function(
4-
Location
6+
$log,
7+
Changes,
8+
Location,
9+
Settings
510
) {
611
'use strict';
712
'ngInject';
813

9-
const dataContext = cht.getRemoteDataContext(Location.rootUrl);
10-
return Object.assign( // NoSONAR
11-
{},
12-
dataContext,
13-
{ getDatasource: () => cht.getDatasource(dataContext) }
14-
);
14+
return Settings().then(initialSettings => {
15+
let latestSettings = initialSettings;
16+
Changes({
17+
key: 'data-context-settings',
18+
filter: change => change.id === DOC_IDS.SETTINGS,
19+
callback: () => Settings()
20+
.then(settings => latestSettings = settings)
21+
.catch(err => $log.error('Failed to refresh settings', err))
22+
});
23+
24+
const settingsService = { getAll: () => latestSettings };
25+
const dataContext = cht.getRemoteDataContext(settingsService, Location.rootUrl);
26+
return Object.assign( // NoSONAR
27+
{},
28+
dataContext,
29+
{ getDatasource: () => cht.getDatasource(dataContext) }
30+
);
31+
});
1532
});

admin/src/js/services/search.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ const Search = require('@medic/search');
1414

1515
'ngInject';
1616

17-
return function() {
18-
return Search(DB(), DataContext);
19-
};
17+
const searchPromise = DataContext.then(dataContext => Search(DB(), dataContext));
18+
const doSearch = (...args) => searchPromise.then(search => search(...args));
19+
return () => doSearch;
2020
});
2121

2222
angular.module('inboxServices').factory('Search',

0 commit comments

Comments
 (0)