/**
   * Return a list of edorgs authorized to use the app, or null if the application is approved for
   * all edorgs
   *
   * @param clientId
   * @return
   */
  public Set<String> getAuthorizingEdOrgsForApp(String clientId) {
    // This is called before the SLIPrincipal has been set, so use TenantContext to get tenant
    // rather than SLIPrincipal on SecurityContext
    Entity app =
        repo.findOne(
            "application", new NeutralQuery(new NeutralCriteria("client_id", "=", clientId)));

    if (app == null) {
      return Collections.EMPTY_SET;
    }

    if (isAuthorizedForAllEdorgs(app)) {
      LOG.debug("App {} is authorized for all edorgs", clientId);
      return null;
    }

    NeutralQuery appAuthCollQuery =
        new NeutralQuery(new NeutralCriteria("applicationId", "=", app.getEntityId()));
    Entity authEntry = repo.findOne("applicationAuthorization", appAuthCollQuery);
    if (authEntry != null) {
      return AppAuthHelper.getAuthorizedEdOrgIds(authEntry);
    } else {
      return new HashSet<String>();
    }
  }
  @Test
  public void testUpdateRetry() {
    TenantContext.setTenantId("SLIUnitTest");
    repository.deleteAll("student", null);

    DBObject indexKeys = new BasicDBObject("body.cityOfBirth", 1);
    mongoTemplate.getCollection("student").ensureIndex(indexKeys);

    repository.create("student", buildTestStudentEntity());

    Entity entity = repository.findOne("student", new NeutralQuery());
    Map<String, Object> studentBody = entity.getBody();
    studentBody.put("cityOfBirth", "ABC");

    Entity studentEntity =
        new MongoEntity("student", entity.getEntityId(), studentBody, entity.getMetaData());
    repository.updateWithRetries("student", studentEntity, 5);

    NeutralQuery neutralQuery = new NeutralQuery();
    neutralQuery.addCriteria(new NeutralCriteria("cityOfBirth=ABC"));
    assertEquals(1, repository.count("student", neutralQuery));

    repository.deleteAll("student", null);
    mongoTemplate.getCollection("student").dropIndex(indexKeys);
  }
  /**
   * Get the list of authorized apps for the user based on the user's LEA.
   *
   * <p>No additional filtering is done on the results. E.g. if a user is a non-admin, the admin
   * apps will still show up in the list, or if an app is disabled it will still show up.
   *
   * @param principal
   * @return list of app IDs, or null if it couldn't be determined
   */
  @SuppressWarnings("unchecked")
  public boolean isAuthorizedForApp(Entity app, SLIPrincipal principal) {

    if (principal.isAdminRealmAuthenticated()) {
      return isAdminVisible(app);
    } else {
      if (isAutoAuthorized(app)) {
        return true;
      } else if (!isOperatorApproved(app)) {
        return false;
      } else {
        Set<String> edOrgs = helper.locateDirectEdorgs(principal.getEntity());
        NeutralQuery appAuthCollQuery = new NeutralQuery();
        appAuthCollQuery.addCriteria(new NeutralCriteria("applicationId", "=", app.getEntityId()));
        appAuthCollQuery.addCriteria(
            new NeutralCriteria("edorgs.authorizedEdorg", NeutralCriteria.CRITERIA_IN, edOrgs));
        Entity authorizedApps = repo.findOne("applicationAuthorization", appAuthCollQuery);
        if (authorizedApps != null) {
          if (isAutoApproved(app)) {
            return true;
          } else {
            // query approved edorgs
            List<String> approvedDistricts =
                new ArrayList<String>((List<String>) app.getBody().get("authorized_ed_orgs"));
            List<String> myDistricts = helper.getDistricts(edOrgs);
            approvedDistricts.retainAll(myDistricts);
            return !approvedDistricts.isEmpty();
          }
        }
      }
    }
    return false;
  }
 boolean schoolLineageIs(String schoolId, Set<String> expectedEdOrgs) {
   NeutralQuery neutralQuery = new NeutralQuery();
   neutralQuery.addCriteria(new NeutralCriteria("_id", NeutralCriteria.OPERATOR_EQUAL, schoolId));
   Entity school = repository.findOne(EntityNames.EDUCATION_ORGANIZATION, neutralQuery);
   ArrayList<String> edOrgs = (ArrayList<String>) school.getMetaData().get("edOrgs");
   if (edOrgs == null) {
     return expectedEdOrgs.isEmpty();
   } else if (!expectedEdOrgs.equals(new HashSet<String>(edOrgs))) {
     System.out.println(
         "School edOrg lineage incorrect. Expected " + expectedEdOrgs + ", got " + edOrgs);
     return false;
   }
   return true;
 }
  private String insertAttendanceEventData(boolean skipFirst) {
    // Clear attendance collection.
    repository.deleteAll("attendance", null);

    // Populate the attendance record to be deleted, and add it to the db.
    Map<String, Object> attendanceMap = new HashMap<String, Object>();
    List<Map<String, String>> attendanceEvents = getAttendanceEvents(skipFirst);
    attendanceMap.put("attendanceEvent", attendanceEvents);
    attendanceMap.put("schoolId", "schoolId1");
    attendanceMap.put("schoolYear", "2011-2012");
    attendanceMap.put("studentId", "studentId1");
    repository.create("attendance", attendanceMap);

    // Get the db id of the attendance record; there is only one.
    NeutralQuery neutralQuery = new NeutralQuery();
    Entity attendance = repository.findOne("attendance", neutralQuery);
    return attendance.getEntityId();
  }
  private String prepareSafeDeleteGradingPeriodLeafData() {
    DBObject indexKeys = new BasicDBObject("body.beginDate", 1);
    mongoTemplate.getCollection("gradingPeriod").ensureIndex(indexKeys);

    // create a minimal gradingPeriod document
    Map<String, Object> gradingPeriodIdentity = new HashMap<String, Object>();
    gradingPeriodIdentity.put("gradingPeriod", "gradingPeriod1");
    gradingPeriodIdentity.put("schoolYear", "2011-2012");
    gradingPeriodIdentity.put("schoolId", "schoolId1");
    Map<String, Object> gradingPeriodBody = new HashMap<String, Object>();
    gradingPeriodBody.put("gradingPeriodIdentity", gradingPeriodIdentity);
    gradingPeriodBody.put("beginDate", "beginDate1");
    repository.create("gradingPeriod", gradingPeriodBody);

    // get the db id of the gradingPeriod - there is only one
    NeutralQuery neutralQuery = new NeutralQuery();
    Entity gradingPeriod1 = repository.findOne("gradingPeriod", neutralQuery);
    return gradingPeriod1.getEntityId();
  }
  @Override
  public SLIPrincipal locate(
      String tenantId, String externalUserId, String userType, String clientId) {
    LOG.info("Locating user {}@{} of type: {}", new Object[] {externalUserId, tenantId, userType});
    SLIPrincipal user = new SLIPrincipal(externalUserId + "@" + tenantId);
    user.setExternalId(externalUserId);
    user.setTenantId(tenantId);
    user.setUserType(userType);

    TenantContext.setTenantId(tenantId);

    if (EntityNames.STUDENT.equals(userType)) {
      NeutralQuery neutralQuery =
          new NeutralQuery(
              new NeutralCriteria(
                  ParameterConstants.STUDENT_UNIQUE_STATE_ID,
                  NeutralCriteria.OPERATOR_EQUAL,
                  externalUserId));
      neutralQuery.setOffset(0);
      neutralQuery.setLimit(1);
      user.setEntity(repo.findOne(EntityNames.STUDENT, neutralQuery, true));
    } else if (EntityNames.PARENT.equals(userType)) {
      NeutralQuery neutralQuery =
          new NeutralQuery(
              new NeutralCriteria(
                  ParameterConstants.PARENT_UNIQUE_STATE_ID,
                  NeutralCriteria.OPERATOR_EQUAL,
                  externalUserId));
      neutralQuery.setOffset(0);
      neutralQuery.setLimit(1);
      user.setEntity(repo.findOne(EntityNames.PARENT, neutralQuery, true));
    } else if (isStaff(userType)) {

      NeutralQuery neutralQuery = new NeutralQuery();
      neutralQuery.setOffset(0);
      neutralQuery.setLimit(1);
      neutralQuery.addCriteria(
          new NeutralCriteria(
              ParameterConstants.STAFF_UNIQUE_STATE_ID,
              NeutralCriteria.OPERATOR_EQUAL,
              externalUserId));

      Iterable<Entity> staff = repo.findAll(EntityNames.STAFF, neutralQuery);

      if (staff != null && staff.iterator().hasNext()) {
        Entity entity = staff.iterator().next();
        Set<String> edorgs = edorgHelper.locateDirectEdorgs(entity);
        if (edorgs.size() == 0) {
          LOG.warn("User {} is not currently associated to a school/edorg", user.getId());
          throw new APIAccessDeniedException(
              "User is not currently associated to a school/edorg", user, clientId);
        }
        user.setEntity(entity);
      }
    }

    if (user.getEntity() == null) {
      LOG.warn("Failed to locate user {} in the datastore", user.getId());
      Entity entity =
          new MongoEntity(
              "user",
              SLIPrincipal.NULL_ENTITY_ID,
              new HashMap<String, Object>(),
              new HashMap<String, Object>());
      user.setEntity(entity);
    } else {
      LOG.info(
          "Matched user: {}@{} -> {}",
          new Object[] {externalUserId, tenantId, user.getEntity().getEntityId()});
    }

    return user;
  }