diff --git a/modules/api/functional_test/live_tests/membership/list_my_groups_test.py b/modules/api/functional_test/live_tests/membership/list_my_groups_test.py index 4ef0b018a..5b33135f3 100644 --- a/modules/api/functional_test/live_tests/membership/list_my_groups_test.py +++ b/modules/api/functional_test/live_tests/membership/list_my_groups_test.py @@ -9,6 +9,7 @@ from vinyldns_context import VinylDNSTestContext class ListGroupsSearchContext(object): def __init__(self): self.client = VinylDNSClient(VinylDNSTestContext.vinyldns_url, access_key='listGroupAccessKey', secret_key='listGroupSecretKey') + self.support_user_client = VinylDNSClient(VinylDNSTestContext.vinyldns_url, 'supportUserAccessKey', 'supportUserSecretKey') self.tear_down() # ensures that the environment is clean before starting try: @@ -29,6 +30,15 @@ class ListGroupsSearchContext(object): pass raise + def verify_ignore_access(self, results): + assert_that(results, has_length(3)) # 3 fields + + assert_that(len(results['groups']), greater_than(50)) + assert_that(results, is_not(has_key('groupNameFilter'))) + assert_that(results, is_not(has_key('startFrom'))) + assert_that(results, is_not(has_key('nextId'))) + assert_that(results['maxItems'], is_(100)) + def tear_down(self): clear_zones(self.client) clear_groups(self.client) @@ -51,7 +61,7 @@ def test_list_my_groups_no_parameters(list_my_groups_context): results = list_my_groups_context.client.list_my_groups(status=200) - assert_that(results, has_length(2)) # 2 fields + assert_that(results, has_length(3)) # 3 fields assert_that(results['groups'], has_length(50)) assert_that(results, is_not(has_key('groupNameFilter'))) @@ -70,7 +80,7 @@ def test_get_my_groups_using_old_account_auth(list_my_groups_context): Test passing in an account will return an empty set """ results = list_my_groups_context.client.list_my_groups(status=200) - assert_that(results, has_length(2)) + assert_that(results, has_length(3)) assert_that(results, is_not(has_key('groupNameFilter'))) assert_that(results, is_not(has_key('startFrom'))) assert_that(results, is_not(has_key('nextId'))) @@ -83,7 +93,7 @@ def test_list_my_groups_max_items(list_my_groups_context): """ results = list_my_groups_context.client.list_my_groups(max_items=5, status=200) - assert_that(results, has_length(3)) # 3 fields + assert_that(results, has_length(4)) # 4 fields assert_that(results, has_key('groups')) assert_that(results, is_not(has_key('groupNameFilter'))) @@ -98,7 +108,7 @@ def test_list_my_groups_paging(list_my_groups_context): """ results=list_my_groups_context.client.list_my_groups(max_items=20, status=200) - assert_that(results, has_length(3)) # 3 fields + assert_that(results, has_length(4)) # 4 fields assert_that(results, has_key('groups')) assert_that(results, is_not(has_key('groupNameFilter'))) assert_that(results, is_not(has_key('startFrom'))) @@ -110,7 +120,7 @@ def test_list_my_groups_paging(list_my_groups_context): results = list_my_groups_context.client.list_my_groups(max_items=20, start_from=results['nextId'], status=200) if 'nextId' in results: - assert_that(results, has_length(4)) # 4 fields + assert_that(results, has_length(5)) # 5 fields assert_that(results, has_key('groups')) assert_that(results, is_not(has_key('groupNameFilter'))) assert_that(results['startFrom'], is_(prev['nextId'])) @@ -118,7 +128,7 @@ def test_list_my_groups_paging(list_my_groups_context): assert_that(results['maxItems'], is_(20)) else: - assert_that(results, has_length(3)) # 3 fields + assert_that(results, has_length(4)) # 4 fields assert_that(results, has_key('groups')) assert_that(results, is_not(has_key('groupNameFilter'))) assert_that(results['startFrom'], is_(prev['nextId'])) @@ -132,7 +142,7 @@ def test_list_my_groups_filter_matches(list_my_groups_context): """ results = list_my_groups_context.client.list_my_groups(group_name_filter="test-list-my-groups-01", status=200) - assert_that(results, has_length(3)) # 3 fields + assert_that(results, has_length(4)) # 4 fields assert_that(results['groups'], has_length(10)) assert_that(results['groupNameFilter'], is_('test-list-my-groups-01')) @@ -163,3 +173,35 @@ def test_list_my_groups_no_deleted(list_my_groups_context): for g in results['groups']: assert_that(g['status'], is_not('Deleted')) +def test_list_my_groups_with_ignore_access_true(list_my_groups_context): + """ + Test that we can get all the groups whether a user is a member or not + """ + + results = list_my_groups_context.client.list_my_groups(ignore_access=True, status=200) + + list_my_groups_context.verify_ignore_access(results) + + my_results = list_my_groups_context.client.list_my_groups(status=200) + my_results['groups'] = sorted(my_results['groups'], key=lambda x: x['name']) + + for i in range(0, 50): + assert_that(my_results['groups'][i]['name'], is_("test-list-my-groups-{0:0>3}".format(i))) + +def test_list_my_groups_as_support_user(list_my_groups_context): + """ + Test that we can get all the groups as a support user, even without ignore_access + """ + + results = list_my_groups_context.support_user_client.list_my_groups(status=200) + + list_my_groups_context.verify_ignore_access(results) + +def test_list_my_groups_as_support_user_with_ignore_access_true(list_my_groups_context): + """ + Test that we can get all the groups as a support user + """ + + results = list_my_groups_context.support_user_client.list_my_groups(ignore_access=True, status=200) + + list_my_groups_context.verify_ignore_access(results) diff --git a/modules/api/functional_test/vinyldns_python.py b/modules/api/functional_test/vinyldns_python.py index e8c3eeb55..b4cac4477 100644 --- a/modules/api/functional_test/vinyldns_python.py +++ b/modules/api/functional_test/vinyldns_python.py @@ -226,12 +226,13 @@ class VinylDNSClient(object): return data - def list_my_groups(self, group_name_filter=None, start_from=None, max_items=None, **kwargs): + def list_my_groups(self, group_name_filter=None, start_from=None, max_items=None, ignore_access=False, **kwargs): """ Retrieves my groups :param start_from: the start key of the page :param max_items: the page limit :param group_name_filter: only returns groups whose names contain filter string + :param ignore_access: determines if groups should be retrieved based on requester's membership :return: the content of the response """ @@ -242,6 +243,8 @@ class VinylDNSClient(object): args.append(u'startFrom={0}'.format(start_from)) if max_items is not None: args.append(u'maxItems={0}'.format(max_items)) + if ignore_access is not False: + args.append(u'ignoreAccess={0}'.format(ignore_access)) url = urljoin(self.index_url, u'/groups') + u'?' + u'&'.join(args) response, data = self.make_request(url, u'GET', self.headers, **kwargs) diff --git a/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipProtocol.scala b/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipProtocol.scala index 676164875..6e69dabec 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipProtocol.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipProtocol.scala @@ -140,7 +140,8 @@ final case class ListMyGroupsResponse( groupNameFilter: Option[String] = None, startFrom: Option[String] = None, nextId: Option[String] = None, - maxItems: Int) + maxItems: Int, + ignoreAccess: Boolean) final case class GroupNotFoundError(msg: String) extends Throwable(msg) diff --git a/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipService.scala b/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipService.scala index 9d7b5e7aa..f9e3d619e 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipService.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipService.scala @@ -135,16 +135,17 @@ class MembershipService( groupNameFilter: Option[String], startFrom: Option[String], maxItems: Int, - authPrincipal: AuthPrincipal): Result[ListMyGroupsResponse] = { + authPrincipal: AuthPrincipal, + ignoreAccess: Boolean): Result[ListMyGroupsResponse] = { val groupsCall = - if (authPrincipal.isSystemAdmin) { + if (authPrincipal.isSystemAdmin || ignoreAccess) { groupRepo.getAllGroups() } else { groupRepo.getGroups(authPrincipal.memberGroupIds.toSet) } groupsCall.map { grp => - pageListGroupsResponse(grp.toList, groupNameFilter, startFrom, maxItems) + pageListGroupsResponse(grp.toList, groupNameFilter, startFrom, maxItems, ignoreAccess) } }.toResult @@ -152,7 +153,8 @@ class MembershipService( allGroups: Seq[Group], groupNameFilter: Option[String], startFrom: Option[String], - maxItems: Int): ListMyGroupsResponse = { + maxItems: Int, + ignoreAccess: Boolean): ListMyGroupsResponse = { val allMyGroups = allGroups .filter(_.status == GroupStatus.Active) .sortBy(_.id) @@ -165,7 +167,7 @@ class MembershipService( val nextId = if (filtered.length > maxItems) Some(filtered(maxItems - 1).id) else None val groups = filtered.take(maxItems) - ListMyGroupsResponse(groups, groupNameFilter, startFrom, nextId, maxItems) + ListMyGroupsResponse(groups, groupNameFilter, startFrom, nextId, maxItems, ignoreAccess) } def getGroupActivity( diff --git a/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipServiceAlgebra.scala b/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipServiceAlgebra.scala index b5961d32a..b5c7245d7 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipServiceAlgebra.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/membership/MembershipServiceAlgebra.scala @@ -42,7 +42,8 @@ trait MembershipServiceAlgebra { groupNameFilter: Option[String], startFrom: Option[String], maxItems: Int, - authPrincipal: AuthPrincipal): Result[ListMyGroupsResponse] + authPrincipal: AuthPrincipal, + ignoreAccess: Boolean): Result[ListMyGroupsResponse] def listMembers( groupId: String, diff --git a/modules/api/src/main/scala/vinyldns/api/route/MembershipRouting.scala b/modules/api/src/main/scala/vinyldns/api/route/MembershipRouting.scala index a5b6a0746..29e857fef 100644 --- a/modules/api/src/main/scala/vinyldns/api/route/MembershipRouting.scala +++ b/modules/api/src/main/scala/vinyldns/api/route/MembershipRouting.scala @@ -69,8 +69,16 @@ class MembershipRoute( } } ~ (get & monitor("Endpoint.listMyGroups")) { - parameters("startFrom".?, "maxItems".as[Int].?(DEFAULT_MAX_ITEMS), "groupNameFilter".?) { - (startFrom: Option[String], maxItems: Int, groupNameFilter: Option[String]) => + parameters( + "startFrom".?, + "maxItems".as[Int].?(DEFAULT_MAX_ITEMS), + "groupNameFilter".?, + "ignoreAccess".as[Boolean].?(false)) { + ( + startFrom: Option[String], + maxItems: Int, + groupNameFilter: Option[String], + ignoreAccess: Boolean) => { handleRejections(invalidQueryHandler) { validate( @@ -81,8 +89,9 @@ class MembershipRoute( """.stripMargin ) { authenticateAndExecute(membershipService - .listMyGroups(groupNameFilter, startFrom, maxItems, _)) { groups => - complete(StatusCodes.OK, groups) + .listMyGroups(groupNameFilter, startFrom, maxItems, _, ignoreAccess)) { + groups => + complete(StatusCodes.OK, groups) } } } diff --git a/modules/api/src/test/scala/vinyldns/api/domain/membership/MembershipServiceSpec.scala b/modules/api/src/test/scala/vinyldns/api/domain/membership/MembershipServiceSpec.scala index 312405a56..1c2327520 100644 --- a/modules/api/src/test/scala/vinyldns/api/domain/membership/MembershipServiceSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/domain/membership/MembershipServiceSpec.scala @@ -529,14 +529,15 @@ class MembershipServiceSpec .when(mockGroupRepo) .getGroups(any[Set[String]]) val result: ListMyGroupsResponse = - rightResultOf(underTest.listMyGroups(None, None, 100, listOfDummyGroupsAuth).value) + rightResultOf(underTest.listMyGroups(None, None, 100, listOfDummyGroupsAuth, false).value) verify(mockGroupRepo, never()).getAllGroups() result shouldBe ListMyGroupsResponse( groups = listOfDummyGroupInfo.take(100), None, None, nextId = Some(listOfDummyGroups(99).id), - maxItems = 100) + maxItems = 100, + ignoreAccess = false) } "return only return groups whose name matches the filter" in { doReturn(IO.pure(listOfDummyGroups.toSet)) @@ -548,14 +549,16 @@ class MembershipServiceSpec groupNameFilter = Some("name-dummy01"), startFrom = None, maxItems = 100, - listOfDummyGroupsAuth) + listOfDummyGroupsAuth, + false) .value) result shouldBe ListMyGroupsResponse( groups = listOfDummyGroupInfo.slice(10, 20), groupNameFilter = Some("name-dummy01"), startFrom = None, nextId = None, - maxItems = 100) + maxItems = 100, + ignoreAccess = false) } "return only return groups after startFrom" in { doReturn(IO.pure(listOfDummyGroups.toSet)) @@ -567,14 +570,16 @@ class MembershipServiceSpec groupNameFilter = None, startFrom = Some(listOfDummyGroups(99).id), maxItems = 100, - listOfDummyGroupsAuth) + listOfDummyGroupsAuth, + ignoreAccess = false) .value) result shouldBe ListMyGroupsResponse( groups = listOfDummyGroupInfo.slice(100, 200), groupNameFilter = None, startFrom = Some(listOfDummyGroups(99).id), nextId = None, - maxItems = 100) + maxItems = 100, + ignoreAccess = false) } "return only return maxItems groups" in { doReturn(IO.pure(listOfDummyGroups.toSet)) @@ -586,35 +591,65 @@ class MembershipServiceSpec groupNameFilter = None, startFrom = None, maxItems = 10, - listOfDummyGroupsAuth) + listOfDummyGroupsAuth, + ignoreAccess = false) .value) result shouldBe ListMyGroupsResponse( groups = listOfDummyGroupInfo.slice(0, 10), groupNameFilter = None, startFrom = None, nextId = Some(listOfDummyGroups(9).id), - maxItems = 10) + maxItems = 10, + ignoreAccess = false) } "return an empty set if the user is not a member of any groups" in { doReturn(IO.pure(Set())).when(mockGroupRepo).getGroups(any[Set[String]]) val result: ListMyGroupsResponse = - rightResultOf(underTest.listMyGroups(None, None, 100, notAuth).value) - result shouldBe ListMyGroupsResponse(Seq(), None, None, None, 100) + rightResultOf(underTest.listMyGroups(None, None, 100, notAuth, false).value) + result shouldBe ListMyGroupsResponse(Seq(), None, None, None, 100, false) } - "return groups from the database for super users" in { + "return all groups from the database if ignoreAccess is true" in { doReturn(IO.pure(Set(okGroup, dummyGroup))).when(mockGroupRepo).getAllGroups() val result: ListMyGroupsResponse = - rightResultOf(underTest.listMyGroups(None, None, 100, superUserAuth).value) + rightResultOf(underTest.listMyGroups(None, None, 100, notAuth, true).value) verify(mockGroupRepo).getAllGroups() result.groups should contain theSameElementsAs Seq( GroupInfo(dummyGroup), GroupInfo(okGroup)) } - "return groups from the database for support users" in { + "return all groups from the database for super users even if ignoreAccess is false" in { + doReturn(IO.pure(Set(okGroup, dummyGroup))).when(mockGroupRepo).getAllGroups() + val result: ListMyGroupsResponse = + rightResultOf(underTest.listMyGroups(None, None, 100, superUserAuth, false).value) + verify(mockGroupRepo).getAllGroups() + result.groups should contain theSameElementsAs Seq( + GroupInfo(dummyGroup), + GroupInfo(okGroup)) + } + "return all groups from the database for super users if ignoreAccess is true" in { + doReturn(IO.pure(Set(okGroup, dummyGroup))).when(mockGroupRepo).getAllGroups() + val result: ListMyGroupsResponse = + rightResultOf(underTest.listMyGroups(None, None, 100, superUserAuth, true).value) + verify(mockGroupRepo).getAllGroups() + result.groups should contain theSameElementsAs Seq( + GroupInfo(dummyGroup), + GroupInfo(okGroup)) + } + "return all groups from the database for support users even if ignoreAccess is false" in { val supportAuth = AuthPrincipal(okUser.copy(isSupport = true), Seq()) doReturn(IO.pure(Set(okGroup, dummyGroup))).when(mockGroupRepo).getAllGroups() val result: ListMyGroupsResponse = - rightResultOf(underTest.listMyGroups(None, None, 100, supportAuth).value) + rightResultOf(underTest.listMyGroups(None, None, 100, supportAuth, false).value) + verify(mockGroupRepo).getAllGroups() + result.groups should contain theSameElementsAs Seq( + GroupInfo(dummyGroup), + GroupInfo(okGroup)) + } + "return all groups from the database for support users if ignoreAccess is true" in { + val supportAuth = AuthPrincipal(okUser.copy(isSupport = true), Seq()) + doReturn(IO.pure(Set(okGroup, dummyGroup))).when(mockGroupRepo).getAllGroups() + val result: ListMyGroupsResponse = + rightResultOf(underTest.listMyGroups(None, None, 100, supportAuth, true).value) verify(mockGroupRepo).getAllGroups() result.groups should contain theSameElementsAs Seq( GroupInfo(dummyGroup), @@ -626,8 +661,8 @@ class MembershipServiceSpec .when(mockGroupRepo) .getGroups(any[Set[String]]) val result: ListMyGroupsResponse = - rightResultOf(underTest.listMyGroups(None, None, 100, deletedGroupAuth).value) - result shouldBe ListMyGroupsResponse(Seq(), None, None, None, 100) + rightResultOf(underTest.listMyGroups(None, None, 100, deletedGroupAuth, false).value) + result shouldBe ListMyGroupsResponse(Seq(), None, None, None, 100, false) } } diff --git a/modules/api/src/test/scala/vinyldns/api/route/MembershipRoutingSpec.scala b/modules/api/src/test/scala/vinyldns/api/route/MembershipRoutingSpec.scala index 33641a549..6eed8911a 100644 --- a/modules/api/src/test/scala/vinyldns/api/route/MembershipRoutingSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/route/MembershipRoutingSpec.scala @@ -188,15 +188,16 @@ class MembershipRoutingSpec "return a 200 response with the groups when no optional parameters are used" in { val twoUserGroupInfo = GroupInfo(twoUserGroup) doReturn( - result(ListMyGroupsResponse(Seq(okGroupInfo, twoUserGroupInfo), None, None, None, 100))) + result( + ListMyGroupsResponse(Seq(okGroupInfo, twoUserGroupInfo), None, None, None, 100, false))) .when(membershipService) - .listMyGroups(None, None, 100, okAuth) + .listMyGroups(None, None, 100, okAuth, false) Get("/groups") ~> Route.seal(membershipRoute) ~> check { status shouldBe StatusCodes.OK val result = responseAs[ListMyGroupsResponse] val expected = - ListMyGroupsResponse(Seq(okGroupInfo, twoUserGroupInfo), None, None, None, 100) + ListMyGroupsResponse(Seq(okGroupInfo, twoUserGroupInfo), None, None, None, 100, false) result shouldBe expected } @@ -209,13 +210,15 @@ class MembershipRoutingSpec groupNameFilter = Some("ok"), startFrom = Some("anyString"), nextId = None, - maxItems = 100))) + maxItems = 100, + ignoreAccess = false))) .when(membershipService) .listMyGroups( groupNameFilter = Some("ok"), startFrom = Some("anyString"), maxItems = 100, - okAuth) + okAuth, + ignoreAccess = false) Get("/groups?startFrom=anyString&maxItems=100&groupNameFilter=ok") ~> Route.seal( membershipRoute) ~> check { status shouldBe StatusCodes.OK @@ -226,7 +229,8 @@ class MembershipRoutingSpec groupNameFilter = Some("ok"), startFrom = Some("anyString"), maxItems = 100, - nextId = None) + nextId = None, + ignoreAccess = false) result shouldBe expected } @@ -244,7 +248,7 @@ class MembershipRoutingSpec "return a 500 response when fails" in { doReturn(result(new IllegalArgumentException("fail"))) .when(membershipService) - .listMyGroups(None, None, 100, okAuth) + .listMyGroups(None, None, 100, okAuth, false) Get("/groups") ~> Route.seal(membershipRoute) ~> check { status shouldBe StatusCodes.InternalServerError diff --git a/modules/portal/app/controllers/VinylDNS.scala b/modules/portal/app/controllers/VinylDNS.scala index 7267fa57f..b993996db 100644 --- a/modules/portal/app/controllers/VinylDNS.scala +++ b/modules/portal/app/controllers/VinylDNS.scala @@ -67,6 +67,7 @@ object VinylDNS { lastName: Option[String], email: Option[String], isSuper: Boolean, + isSupport: Boolean, id: String, lockStatus: LockStatus) object UserInfo { @@ -77,6 +78,7 @@ object VinylDNS { lastName = user.lastName, email = user.email, isSuper = user.isSuper, + isSupport = user.isSupport, id = user.id, lockStatus = user.lockStatus ) @@ -218,7 +220,7 @@ class VinylDNS @Inject()( }) } - def getMyGroups(): Action[AnyContent] = userAction.async { implicit request => + def getGroups(): Action[AnyContent] = userAction.async { implicit request => val queryParameters = new HashMap[String, java.util.List[String]]() for { (name, values) <- request.queryString diff --git a/modules/portal/app/views/groups/groups.scala.html b/modules/portal/app/views/groups/groups.scala.html index b6227ad46..a3182f256 100644 --- a/modules/portal/app/views/groups/groups.scala.html +++ b/modules/portal/app/views/groups/groups.scala.html @@ -22,7 +22,17 @@ -
+ + +
+ + +
+
+
@@ -63,7 +73,7 @@ {{group.description}}
- + View Edit @@ -80,6 +90,11 @@
+
+
+
+ +
diff --git a/modules/portal/conf/routes b/modules/portal/conf/routes index df8d5ec1f..d12e52c30 100644 --- a/modules/portal/conf/routes +++ b/modules/portal/conf/routes @@ -42,7 +42,7 @@ PUT /api/zones/:zid/recordsets/:rid @controllers.VinylDNS.updateRec GET /api/zones/:id/recordsetchanges @controllers.VinylDNS.listRecordSetChanges(id: String) -GET /api/groups @controllers.VinylDNS.getMyGroups +GET /api/groups @controllers.VinylDNS.getGroups GET /api/groups/:gid @controllers.VinylDNS.getGroup(gid: String) POST /api/groups @controllers.VinylDNS.newGroup PUT /api/groups/:gid @controllers.VinylDNS.updateGroup(gid: String) diff --git a/modules/portal/public/lib/batch-change/batch-change-new.controller.js b/modules/portal/public/lib/batch-change/batch-change-new.controller.js index ff2557e30..bae3c6f2c 100644 --- a/modules/portal/public/lib/batch-change/batch-change-new.controller.js +++ b/modules/portal/public/lib/batch-change/batch-change-new.controller.js @@ -19,7 +19,7 @@ angular.module('batch-change') .controller('BatchChangeNewController', function($scope, $log, $location, $timeout, $q, batchChangeService, utilityService, groupsService){ - groupsService.getMyGroups() + groupsService.getGroups() .then(function (results) { $scope.myGroups = results['data']['groups']; if ($scope.myGroups.length == 1) { @@ -27,7 +27,7 @@ } }) .catch(function (error) { - handleError(error, 'groupsService::getMyGroups-failure'); + handleError(error, 'groupsService::getGroups-failure'); }); $scope.batch = {}; diff --git a/modules/portal/public/lib/batch-change/batch-change.spec.js b/modules/portal/public/lib/batch-change/batch-change.spec.js index c3118a3eb..69a8d889f 100644 --- a/modules/portal/public/lib/batch-change/batch-change.spec.js +++ b/modules/portal/public/lib/batch-change/batch-change.spec.js @@ -153,7 +153,7 @@ describe('BatchChange', function(){ deferred = $q.defer(); spyOn(batchChangeService, 'createBatchChange').and.returnValue(deferred.promise); - groupsService.getMyGroups = function() { + groupsService.getGroups = function() { return $q.when({ data: { groups: "all my groups" diff --git a/modules/portal/public/lib/controllers/controller.groups.js b/modules/portal/public/lib/controllers/controller.groups.js index a7e35cb57..af60d605a 100644 --- a/modules/portal/public/lib/controllers/controller.groups.js +++ b/modules/portal/public/lib/controllers/controller.groups.js @@ -23,6 +23,7 @@ angular.module('controller.groups', []).controller('GroupsController', function $scope.groups = { items: [] }; $scope.groupsLoaded = false; $scope.alerts = []; + $scope.ignoreAccess = false; function handleError(error, type) { var alert = utilityService.failure(error, type); @@ -101,19 +102,28 @@ angular.module('controller.groups', []).controller('GroupsController', function }); }; + $scope.allGroups = function() { + $scope.ignoreAccess = true; + $scope.refresh(); + } + + $scope.myGroups = function() { + $scope.ignoreAccess = false; + $scope.refresh(); + } + $scope.refresh = function () { - //get users groups function success(result) { - $log.log('getMyGroups:refresh-success', result); + $log.log('getGroups:refresh-success', result); //update groups $scope.groups.items = result.groups; $scope.groupsLoaded = true; return result; } - return getMyGroups() + getGroups($scope.ignoreAccess) .then(success) .catch(function (error) { - handleError(error, 'getMyGroups::refresh-failure'); + handleError(error, 'getGroups::refresh-failure'); }); }; @@ -131,16 +141,16 @@ angular.module('controller.groups', []).controller('GroupsController', function return true; }; - function getMyGroups() { + function getGroups() { function success(response) { - $log.log('groupsService::getMyGroups-success'); + $log.log('groupsService::getGroups-success'); return response.data; } return groupsService - .getMyGroups() + .getGroups($scope.ignoreAccess) .then(success) .catch(function (error){ - handleError(error, 'groupsService::getMyGroups-failure'); + handleError(error, 'groupsService::getGroups-failure'); }); } @@ -233,7 +243,14 @@ angular.module('controller.groups', []).controller('GroupsController', function return x.id === $scope.profile.id; }); var isSuper = $scope.profile.isSuper; - return (isAdmin || isSuper) ? true : false; + return isAdmin || isSuper; + } + + $scope.canSeeGroup = function(group) { + var isMember = group.members.some(x => x.id === $scope.profile.id); + var isSupport = $scope.profile.isSupport; + var isSuper = $scope.profile.isSuper; + return isMember || isSupport || isSuper; } //get user data on groups view load diff --git a/modules/portal/public/lib/controllers/controller.groups.spec.js b/modules/portal/public/lib/controllers/controller.groups.spec.js index 1be9f3a98..2fa24b30f 100644 --- a/modules/portal/public/lib/controllers/controller.groups.spec.js +++ b/modules/portal/public/lib/controllers/controller.groups.spec.js @@ -31,7 +31,7 @@ describe('Controller: GroupsController', function () { profileService.getAuthenticatedUserData = function() { return $q.when('data') }; - this.groupsService.getMyGroups = function() { + this.groupsService.getGroups = function() { return $q.when({ data: { group: 'mock group' @@ -57,14 +57,14 @@ describe('Controller: GroupsController', function () { groups: "all my groups" } }; - var getMyGroups = spyOn(this.groupsService, 'getMyGroups') + var getGroups = spyOn(this.groupsService, 'getGroups') .and.stub() .and.returnValue(this.q.when(response)); this.scope.refresh(); this.scope.$digest(); - expect(getMyGroups.calls.count()).toBe(1); + expect(getGroups.calls.count()).toBe(1); expect(this.scope.groups.items).toBe("all my groups"); }); diff --git a/modules/portal/public/lib/controllers/controller.manageZones.js b/modules/portal/public/lib/controllers/controller.manageZones.js index d722fd9f4..c5f5e3e3b 100644 --- a/modules/portal/public/lib/controllers/controller.manageZones.js +++ b/modules/portal/public/lib/controllers/controller.manageZones.js @@ -18,7 +18,7 @@ angular.module('controller.manageZones', []) .controller('ManageZonesController', function ($scope, $timeout, $log, recordsService, zonesService, groupsService, profileService, utilityService) { - groupsService.getMyGroupsStored() + groupsService.getGroupsStored() .then(function (results) { $scope.myGroups = results.groups; }) diff --git a/modules/portal/public/lib/controllers/controller.manageZones.spec.js b/modules/portal/public/lib/controllers/controller.manageZones.spec.js index e071f98cd..fef3116ba 100644 --- a/modules/portal/public/lib/controllers/controller.manageZones.spec.js +++ b/modules/portal/public/lib/controllers/controller.manageZones.spec.js @@ -33,7 +33,7 @@ describe('Controller: ManageZonesController', function () { this.recordsService = recordsService; this.profileService = profileService; this.q = $q; - this.groupsService.getMyGroups = function () { + this.groupsService.getGroups = function () { return $q.when({ data: { groups: "all my groups" diff --git a/modules/portal/public/lib/controllers/controller.records.js b/modules/portal/public/lib/controllers/controller.records.js index 120730f9e..487e42989 100644 --- a/modules/portal/public/lib/controllers/controller.records.js +++ b/modules/portal/public/lib/controllers/controller.records.js @@ -350,7 +350,7 @@ angular.module('controller.records', []) function getMembership(){ groupsService - .getMyGroupsStored() + .getGroupsStored() .then( function (results) { $scope.myGroups = results.groups; @@ -358,7 +358,7 @@ angular.module('controller.records', []) determineAdmin() }) .catch(function (error){ - handleError(error, 'groupsService::getMyGroupsStored-failure'); + handleError(error, 'groupsService::getGroupsStored-failure'); }); } diff --git a/modules/portal/public/lib/controllers/controller.records.spec.js b/modules/portal/public/lib/controllers/controller.records.spec.js index c0a2f1fa0..b39dff6e2 100644 --- a/modules/portal/public/lib/controllers/controller.records.spec.js +++ b/modules/portal/public/lib/controllers/controller.records.spec.js @@ -126,7 +126,7 @@ describe('Controller: RecordsController', function () { spyOn(this.recordsService, 'getZone') .and.stub() .and.returnValue(this.q.when({ data: {zone: mockZone}})); - spyOn(this.groupsService, 'getMyGroups') + spyOn(this.groupsService, 'getGroups') .and.stub() .and.returnValue(this.q.when(mockGroups)); this.$httpBackend.when('GET', '/api/users/currentuser').respond({}); @@ -166,7 +166,7 @@ describe('Controller: RecordsController', function () { spyOn(this.recordsService, 'getZone') .and.stub() .and.returnValue(this.q.when({ data: {zone: mockZone}})); - spyOn(this.groupsService, 'getMyGroups') + spyOn(this.groupsService, 'getGroups') .and.stub() .and.returnValue(this.q.when(mockGroups)); this.$httpBackend.when('GET', '/api/users/currentuser').respond({}); @@ -206,7 +206,7 @@ describe('Controller: RecordsController', function () { spyOn(this.recordsService, 'getZone') .and.stub() .and.returnValue(this.q.when({ data: {zone: mockZone}})); - spyOn(this.groupsService, 'getMyGroups') + spyOn(this.groupsService, 'getGroups') .and.stub() .and.returnValue(this.q.when(mockGroups)); this.$httpBackend.when('GET', '/api/users/currentuser').respond({}); @@ -246,7 +246,7 @@ describe('Controller: RecordsController', function () { spyOn(this.recordsService, 'getZone') .and.stub() .and.returnValue(this.q.when({ data: {zone: mockZone}})); - spyOn(this.groupsService, 'getMyGroups') + spyOn(this.groupsService, 'getGroups') .and.stub() .and.returnValue(this.q.when(mockGroups)); this.$httpBackend.when('GET', '/api/users/currentuser').respond({}); diff --git a/modules/portal/public/lib/controllers/controller.zones.js b/modules/portal/public/lib/controllers/controller.zones.js index 06f109482..793bd6187 100644 --- a/modules/portal/public/lib/controllers/controller.zones.js +++ b/modules/portal/public/lib/controllers/controller.zones.js @@ -50,7 +50,7 @@ angular.module('controller.zones', []) $scope.currentZone.transferConnection = {}; }; - groupsService.getMyGroups().then(function (results) { + groupsService.getGroups().then(function (results) { if (results.data) { $scope.myGroups = results.data.groups; $scope.myGroupIds = results.data.groups.map(function(grp) {return grp['id']}); diff --git a/modules/portal/public/lib/controllers/controller.zones.spec.js b/modules/portal/public/lib/controllers/controller.zones.spec.js index 2cf985253..bf4ceabe2 100644 --- a/modules/portal/public/lib/controllers/controller.zones.spec.js +++ b/modules/portal/public/lib/controllers/controller.zones.spec.js @@ -38,7 +38,7 @@ describe('Controller: ZonesController', function () { profileService.getAuthenticatedUserData = function() { return $q.when({data: {}}); }; - groupsService.getMyGroups = function() { + groupsService.getGroups = function() { return $q.when({ data: { groups: [{id: "all my groups"}] diff --git a/modules/portal/public/lib/services/groups/service.groups.js b/modules/portal/public/lib/services/groups/service.groups.js index 97b46aacf..623d5f87f 100644 --- a/modules/portal/public/lib/services/groups/service.groups.js +++ b/modules/portal/public/lib/services/groups/service.groups.js @@ -70,9 +70,9 @@ angular.module('service.groups', []) return $http.delete(url, {headers: utilityService.getCsrfHeader()}); }; - this.getMyGroups = function () { + this.getGroups = function (ignoreAccess) { var url = '/api/groups'; - url = this.urlBuilder(url, { maxItems: 1000}); + url = this.urlBuilder(url, { maxItems: 1000, ignoreAccess: ignoreAccess}); return $http.get(url); }; @@ -82,9 +82,9 @@ angular.module('service.groups', []) return $http.get(url); }; - this.getMyGroupsStored = function () { + this.getGroupsStored = function () { if (_refreshMyGroups || _myGroupsPromise == undefined) { - _myGroupsPromise = this.getMyGroups().then( + _myGroupsPromise = this.getGroups().then( function(response) { _refreshMyGroups = false; return response.data; diff --git a/modules/portal/public/lib/services/groups/service.groups.spec.js b/modules/portal/public/lib/services/groups/service.groups.spec.js index 83dc2168d..1bb8ca8b2 100644 --- a/modules/portal/public/lib/services/groups/service.groups.spec.js +++ b/modules/portal/public/lib/services/groups/service.groups.spec.js @@ -432,26 +432,26 @@ describe('Service: groupsService', function () { this.$httpBackend.flush(); }); - it('getMyGroups method should return 200 with valid groups', function (done) { + it('getGroups method should return 200 with valid groups', function (done) { var url = '/api/groups'; this.$httpBackend.whenRoute('GET', url).respond(200, getJSONFixture('mockGroupList.json')); - this.groupsService.getMyGroups() + this.groupsService.getGroups() .then(function (response) { expect(response.status).toBe(200); done(); }, function (error) { - fail('getMyGroups expected 200, but got ' + response.status.toString()); + fail('getGroups expected 200, but got ' + response.status.toString()); done(); }); this.$httpBackend.flush(); }); - it('getMyGroups method should return 401 with invalid credentials', function (done) { + it('getGroups method should return 401 with invalid credentials', function (done) { var url = '/api/groups'; this.$httpBackend.whenRoute('GET', url).respond(401, "The resource requires authentication, which was not supplied with the request"); - this.groupsService.getMyGroups() + this.groupsService.getGroups() .then(function (response) { - fail("getMyGroups expected 401, but got " + response.status.toString()); + fail("getGroups expected 401, but got " + response.status.toString()); done(); }, function (error) { expect(error.status).toBe(401); @@ -460,14 +460,14 @@ describe('Service: groupsService', function () { this.$httpBackend.flush(); }); - it('getMyGroupsStored should only call the api on its 1st call', function (done) { + it('getGroupsStored should only call the api on its 1st call', function (done) { var url = '/api/groups'; var groupsResult = getJSONFixture('mockGroupList.json'); this.$httpBackend.whenRoute('GET', url).respond(200, groupsResult); - var getMyGroupsCalls = spyOn(this.groupsService, 'getMyGroups').and.callThrough(); - var getMyGroupsStoredCalls = spyOn(this.groupsService, 'getMyGroupsStored').and.callThrough(); + var getGroupsCalls = spyOn(this.groupsService, 'getGroups').and.callThrough(); + var getGroupsStoredCalls = spyOn(this.groupsService, 'getGroupsStored').and.callThrough(); - this.groupsService.getMyGroupsStored().then(function (response) { + this.groupsService.getGroupsStored().then(function (response) { expect(response).toEqual(groupsResult); }, function (error) { fail('getGroup expected 202 with json, but got' + error.status.toString()); @@ -475,42 +475,42 @@ describe('Service: groupsService', function () { this.$httpBackend.flush(); - this.groupsService.getMyGroupsStored().then(function (response) { + this.groupsService.getGroupsStored().then(function (response) { expect(response).toEqual(groupsResult); }, function (error) { fail('getGroup expected 202 with json, but got' + error.status.toString()); }); - expect(getMyGroupsStoredCalls.calls.count()).toBe(2); - expect(getMyGroupsCalls.calls.count()).toBe(1); + expect(getGroupsStoredCalls.calls.count()).toBe(2); + expect(getGroupsCalls.calls.count()).toBe(1); done(); }); - it('getMyGroupsStored should call the api twice when there is an error', function (done) { + it('getGroupsStored should call the api twice when there is an error', function (done) { var url = '/api/groups'; this.$httpBackend.whenRoute('GET', url).respond(401, "The resource requires authentication, which was not supplied with the request"); - var getMyGroupsCalls = spyOn(this.groupsService, 'getMyGroups').and.callThrough(); - var getMyGroupsStoredCalls = spyOn(this.groupsService, 'getMyGroupsStored').and.callThrough(); + var getGroupsCalls = spyOn(this.groupsService, 'getGroups').and.callThrough(); + var getGroupsStoredCalls = spyOn(this.groupsService, 'getGroupsStored').and.callThrough(); - this.groupsService.getMyGroupsStored().then(function (response) { - fail("getMyGroups expected 401, but got " + response.status.toString()); + this.groupsService.getGroupsStored().then(function (response) { + fail("getGroups expected 401, but got " + response.status.toString()); }, function (error) { expect(error.status).toBe(401); }); this.$httpBackend.flush(); - this.groupsService.getMyGroupsStored().then(function (response) { - fail("getMyGroups expected 401, but got " + response.status.toString()); + this.groupsService.getGroupsStored().then(function (response) { + fail("getGroups expected 401, but got " + response.status.toString()); }, function (error) { expect(error.status).toBe(401); }); this.$httpBackend.flush(); - expect(getMyGroupsStoredCalls.calls.count()).toBe(2); - expect(getMyGroupsCalls.calls.count()).toBe(2); + expect(getGroupsStoredCalls.calls.count()).toBe(2); + expect(getGroupsCalls.calls.count()).toBe(2); done(); }); diff --git a/modules/portal/test/controllers/VinylDNSSpec.scala b/modules/portal/test/controllers/VinylDNSSpec.scala index fa84cb24d..6d109e46f 100644 --- a/modules/portal/test/controllers/VinylDNSSpec.scala +++ b/modules/portal/test/controllers/VinylDNSSpec.scala @@ -1418,7 +1418,7 @@ class VinylDNSSpec extends Specification with Mockito with TestApplicationData w client, components, crypto) - val result = underTest.getMyGroups()(FakeRequest(GET, s"/api/groups") + val result = underTest.getGroups()(FakeRequest(GET, s"/api/groups") .withSession("username" -> frodoUser.userName, "accessKey" -> frodoUser.accessKey)) status(result) must beEqualTo(OK) @@ -1443,7 +1443,7 @@ class VinylDNSSpec extends Specification with Mockito with TestApplicationData w client, components, crypto) - val result = underTest.getMyGroups()(FakeRequest(GET, s"/api/groups")) + val result = underTest.getGroups()(FakeRequest(GET, s"/api/groups")) status(result) mustEqual 401 contentAsString(result) must beEqualTo( @@ -1468,7 +1468,7 @@ class VinylDNSSpec extends Specification with Mockito with TestApplicationData w client, components, crypto) - val result = underTest.getMyGroups()( + val result = underTest.getGroups()( FakeRequest(GET, s"/api/groups") .withSession( "username" -> lockedFrodoUser.userName, @@ -1500,7 +1500,7 @@ class VinylDNSSpec extends Specification with Mockito with TestApplicationData w client, components, crypto) - val result = underTest.getMyGroups()(FakeRequest(GET, s"/api/groups") + val result = underTest.getGroups()(FakeRequest(GET, s"/api/groups") .withSession("username" -> frodoUser.userName, "accessKey" -> frodoUser.accessKey)) status(result) must beEqualTo(UNAUTHORIZED)