Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bob/6936 query for admin emails #6962

Merged
merged 10 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package gov.cdc.usds.simplereport.api.model.errors;

import graphql.ErrorClassification;
import graphql.ErrorType;
import graphql.GraphQLError;
import graphql.language.SourceLocation;
import java.util.Collections;
import java.util.List;

/** Exception to throw when a organization does not exist. */
public class NonexistentOrgException extends RuntimeException implements GraphQLError {

private static final long serialVersionUID = 1L;

public NonexistentOrgException() {
super("Cannot find organization.");
}

@Override // should-be-defaulted unused interface method
public List<SourceLocation> getLocations() {
return Collections.emptyList();
}

@Override
public ErrorClassification getErrorType() {
return ErrorType.ExecutionAborted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,10 @@ public Optional<ApiFacility> facility(@Argument UUID id) {
public FacilityStats facilityStats(@Argument UUID facilityId) {
return this._organizationService.getFacilityStats(facilityId);
}

@QueryMapping
@AuthorizationConfiguration.RequireGlobalAdminUser
public List<UUID> getOrgAdminUserIds(@Argument UUID orgId) {
return _organizationService.getOrgAdminUserIds(orgId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import gov.cdc.usds.simplereport.api.model.errors.GenericGraphqlException;
import gov.cdc.usds.simplereport.api.model.errors.IllegalGraphqlArgumentException;
import gov.cdc.usds.simplereport.api.model.errors.IllegalGraphqlFieldAccessException;
import gov.cdc.usds.simplereport.api.model.errors.NonexistentOrgException;
import gov.cdc.usds.simplereport.api.model.errors.NonexistentUserException;
import gov.cdc.usds.simplereport.api.model.errors.OktaAccountUserException;
import gov.cdc.usds.simplereport.api.model.errors.PrivilegeUpdateFacilityAccessException;
Expand Down Expand Up @@ -66,6 +67,12 @@ public DataFetcherExceptionResolver dataFetcherExceptionResolver() {
return Mono.just(singletonList(new GenericGraphqlException(errorMessage, errorPath)));
}

if (exception instanceof NonexistentOrgException) {
String errorMessage =
String.format("header: Cannot find organization.; %s", defaultErrorBody);
return Mono.just(singletonList(new GenericGraphqlException(errorMessage, errorPath)));
}

if (exception instanceof OktaAccountUserException) {
String errorBody = "The user's account needs to be properly setup.";
String errorMessage =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
import gov.cdc.usds.simplereport.api.model.FacilityStats;
import gov.cdc.usds.simplereport.api.model.errors.IllegalGraphqlArgumentException;
import gov.cdc.usds.simplereport.api.model.errors.MisconfiguredUserException;
import gov.cdc.usds.simplereport.api.model.errors.NonexistentOrgException;
import gov.cdc.usds.simplereport.config.AuthorizationConfiguration;
import gov.cdc.usds.simplereport.config.authorization.OrganizationRoleClaims;
import gov.cdc.usds.simplereport.db.model.ApiUser;
import gov.cdc.usds.simplereport.db.model.DeviceType;
import gov.cdc.usds.simplereport.db.model.Facility;
import gov.cdc.usds.simplereport.db.model.FacilityBuilder;
import gov.cdc.usds.simplereport.db.model.Organization;
import gov.cdc.usds.simplereport.db.model.Provider;
import gov.cdc.usds.simplereport.db.model.auxiliary.PersonName;
import gov.cdc.usds.simplereport.db.model.auxiliary.StreetAddress;
import gov.cdc.usds.simplereport.db.repository.ApiUserRepository;
import gov.cdc.usds.simplereport.db.repository.DeviceTypeRepository;
import gov.cdc.usds.simplereport.db.repository.FacilityRepository;
import gov.cdc.usds.simplereport.db.repository.OrganizationRepository;
Expand Down Expand Up @@ -40,6 +43,7 @@
@Slf4j
@RequiredArgsConstructor
public class OrganizationService {
private final ApiUserRepository apiUserRepository;

private final OrganizationRepository organizationRepository;
private final FacilityRepository facilityRepository;
Expand Down Expand Up @@ -466,4 +470,26 @@
public UUID getPermissibleOrgId(UUID orgId) {
return orgId != null ? orgId : getCurrentOrganization().getInternalId();
}

@AuthorizationConfiguration.RequireGlobalAdminUser
public List<UUID> getOrgAdminUserIds(UUID orgId) {
Organization org =
organizationRepository.findById(orgId).orElseThrow(NonexistentOrgException::new);
List<String> adminUserEmails = oktaRepository.fetchAdminUserEmail(org);

return adminUserEmails.stream()
.map(
email -> {
Optional<ApiUser> foundUser = apiUserRepository.findByLoginEmail(email);
if (foundUser.isEmpty()) {
log.warn(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for this change! :D

"Query for admin users in organization ",
orgId,
" found a user in Okta but not in the database. Skipping...");
Fixed Show fixed Hide fixed
}
return foundUser.map(user -> user.getInternalId()).orElse(null);
})
.filter(userId -> userId != null)
.collect(Collectors.toList());
}
}
1 change: 1 addition & 0 deletions backend/src/main/resources/graphql/admin.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extend type Query {
pendingOrganizations: [PendingOrganization!]!
organization(id: ID!): Organization
facilityStats(facilityId: ID!): FacilityStats
getOrgAdminUserIds(orgId: ID!): [ID]
}
extend type Mutation {
resendToReportStream(testEventIds: [ID!]!, fhirOnly: Boolean!): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import gov.cdc.usds.simplereport.api.model.FacilityStats;
import gov.cdc.usds.simplereport.api.model.errors.IllegalGraphqlArgumentException;
import gov.cdc.usds.simplereport.api.model.errors.NonexistentOrgException;
import gov.cdc.usds.simplereport.api.model.errors.OrderingProviderRequiredException;
import gov.cdc.usds.simplereport.config.simplereport.DemoUserConfiguration;
import gov.cdc.usds.simplereport.db.model.DeviceType;
import gov.cdc.usds.simplereport.db.model.Facility;
import gov.cdc.usds.simplereport.db.model.Organization;
import gov.cdc.usds.simplereport.db.model.PatientSelfRegistrationLink;
import gov.cdc.usds.simplereport.db.model.auxiliary.PersonName;
import gov.cdc.usds.simplereport.db.model.auxiliary.StreetAddress;
import gov.cdc.usds.simplereport.db.repository.ApiUserRepository;
import gov.cdc.usds.simplereport.db.repository.DeviceTypeRepository;
import gov.cdc.usds.simplereport.db.repository.FacilityRepository;
import gov.cdc.usds.simplereport.db.repository.OrganizationRepository;
Expand Down Expand Up @@ -52,6 +56,8 @@
@Autowired private DeviceTypeRepository deviceTypeRepository;
@Autowired @SpyBean private OktaRepository oktaRepository;
@Autowired @SpyBean private PersonRepository personRepository;
@Autowired ApiUserRepository _apiUserRepo;
@Autowired private DemoUserConfiguration userConfiguration;

@BeforeEach
void setupData() {
Expand Down Expand Up @@ -452,4 +458,44 @@
assertThat(updatedFacility.getDeviceTypes()).hasSize(2);
}
}

@Test
@WithSimpleReportSiteAdminUser
void getOrgAdminUserIds_success() {
Organization createdOrg = _dataFactory.saveValidOrganization();
List<String> adminUserEmails = oktaRepository.fetchAdminUserEmail(createdOrg);

List<UUID> expectedIds =
adminUserEmails.stream()
.map(email -> _apiUserRepo.findByLoginEmail(email).get().getInternalId())
.collect(Collectors.toList());

List<UUID> adminIds = _service.getOrgAdminUserIds(createdOrg.getInternalId());
assertThat(adminIds).isEqualTo(expectedIds);
}

@Test
@WithSimpleReportSiteAdminUser
void getOrgAdminUserIds_throws_forNonExistentOrg() {
UUID mismatchedUUID = UUID.fromString("5ebf893a-bb57-48ca-8fc2-1ef6b25e465b");
assertThrows(NonexistentOrgException.class, () -> _service.getOrgAdminUserIds(mismatchedUUID));
}

@Test
@WithSimpleReportSiteAdminUser
void getOrgAdminUserIds_skipsUser_forNonExistentUserInOrg() {
Organization createdOrg = _dataFactory.saveValidOrganization();
List<String> listWithAnExtraEmail = oktaRepository.fetchAdminUserEmail(createdOrg);
listWithAnExtraEmail.add("[email protected]");

when(oktaRepository.fetchAdminUserEmail(createdOrg)).thenReturn(listWithAnExtraEmail);
List<UUID> expectedIds =
listWithAnExtraEmail.stream()
.filter(email -> email != "[email protected]")
Fixed Show fixed Hide fixed
.map(email -> _apiUserRepo.findByLoginEmail(email).get().getInternalId())
.collect(Collectors.toList());

List<UUID> adminIds = _service.getOrgAdminUserIds(createdOrg.getInternalId());
assertThat(adminIds).isEqualTo(expectedIds);
}
}
5 changes: 5 additions & 0 deletions frontend/src/generated/graphql.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,7 @@ export type Query = {
facilities?: Maybe<Array<Maybe<Facility>>>;
facility?: Maybe<Facility>;
facilityStats?: Maybe<FacilityStats>;
getOrgAdminUserIds?: Maybe<Array<Maybe<Scalars["ID"]["output"]>>>;
organization?: Maybe<Organization>;
organizationLevelDashboardMetrics?: Maybe<OrganizationLevelDashboardMetrics>;
organizations: Array<Organization>;
Expand Down Expand Up @@ -741,6 +742,10 @@ export type QueryFacilityStatsArgs = {
facilityId: Scalars["ID"]["input"];
};

export type QueryGetOrgAdminUserIdsArgs = {
orgId: Scalars["ID"]["input"];
};

export type QueryOrganizationArgs = {
id: Scalars["ID"]["input"];
};
Expand Down
Loading