/**
   * 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;
  }
  private Set<String> getEdOrgs(Entity principal, boolean filterByOwnership) {
    if (isStaff(principal) || isTeacher(principal)) {
      return getStaffDirectlyAssociatedEdorgs(principal, filterByOwnership);
    } else if (isStudent(principal)) {
      return getStudentsCurrentAssociatedEdOrgs(
          Collections.singleton(principal.getEntityId()), filterByOwnership);
    } else if (isParent(principal)) {
      SLIPrincipal prince = new SLIPrincipal();
      prince.setEntity(principal);
      prince.populateChildren(repo);

      return getStudentsCurrentAssociatedEdOrgs(prince.getOwnedStudentIds(), false);
    }

    return new HashSet<String>();
  }
  @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;
  }