/* (non-Javadoc)
@see org.jasig.services.persondir.IPersonAttributeDao#getPeopleWithMultivaluedAttributes(java.util.Map)
504067
@Override
    @JsonIgnore
    public Set<IPersonAttributes> getPeopleWithMultivaluedAttributes(final Map<String, List<Object>> query,
                                                                     final IPersonAttributeDaoFilter filter) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("invoking getPeopleWithMultivaluedAttributes(" + query + ")");
        }

        final IUsernameAttributeProvider usernameAttributeProvider = super.getUsernameAttributeProvider();
        String uid = usernameAttributeProvider.getUsernameFromQuery(query);
        if (uid == null) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("No username attribute found in query, returning null");
            }

            return null;
        }
        uid = usernameCaseCanonicalizationMode.canonicalize(uid, usernameCaseCanonicalizationLocale);

        String targetName = this.descriptors.getName();
        if (targetName == null) {
            if (this.currentUserProvider != null) {
                targetName = this.currentUserProvider.getCurrentUserName();
            }

            if (targetName == null) {
                this.logger.warn("AdditionalDescriptors has a null name and a null name was returned by the currentUserProvider, returning null. " + this.descriptors);
                return null;
            }
        }

        targetName = usernameCaseCanonicalizationMode.canonicalize(targetName, usernameCaseCanonicalizationLocale);
        if (uid.equals(targetName)) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Adding additional descriptors " + this.descriptors);
            }

            final IPersonAttributes personAttributes = new CaseInsensitiveNamedPersonImpl(targetName, this.descriptors.getAttributes());
            return Collections.singleton(personAttributes);
        }

        return null;
    }
785544
@Override
    @JsonIgnore
    public Set<String> getAvailableQueryAttributes(final IPersonAttributeDaoFilter filter) {
        if (this.queryAttributeMapping == null) {
            return new HashSet<>();
        }

        final Set list = new HashSet(this.queryAttributeMapping.keySet());
        return list;
    }
112761
private void parseMultipartParameters(Map<String, List<FileItem>> fileItemListMap, RequestAdapter requestAdapter) {
        String encoding = requestAdapter.getEncoding();
        MultiValueMap<String, String> parameterMap = new LinkedMultiValueMap<>();
        MultiValueMap<String, FileParameter> fileParameterMap = new LinkedMultiValueMap<>();

        for (Map.Entry<String, List<FileItem>> entry : fileItemListMap.entrySet()) {
            String fieldName = entry.getKey();
            List<FileItem> fileItemList = entry.getValue();
            if (fileItemList != null && !fileItemList.isEmpty()) {
                for (FileItem fileItem : fileItemList) {
                    if (fileItem.isFormField()) {
                        String value = getString(fileItem, encoding);
                        parameterMap.add(fieldName, value);
                    } else {
                        String fileName = fileItem.getName();

                        // Skip file uploads that don't have a file name - meaning that
                        // no file was selected.
                        if (StringUtils.isEmpty(fileName)) {
                            continue;
                        }

                        boolean valid = FilenameUtils.isValidFileExtension(fileName, allowedFileExtensions, deniedFileExtensions);
                        if (!valid) {
                            continue;
                        }

                        MemoryMultipartFileParameter fileParameter = new MemoryMultipartFileParameter(fileItem);
                        fileParameterMap.add(fieldName, fileParameter);

                        if (log.isDebugEnabled()) {
                            log.debug("Found multipart file [" + fileParameter.getFileName() + "] of size " +
                                    fileParameter.getFileSize() + " bytes, stored " +
                                    fileParameter.getStorageDescription());
                        }
                    }
                }
            }
        }

        if (!parameterMap.isEmpty()) {
            for (Map.Entry<String, List<String>> entry : parameterMap.entrySet()) {
                String name = entry.getKey();
                List<String> list = entry.getValue();
                String[] values = list.toArray(new String[0]);
                requestAdapter.setParameter(name, values);
            }
        }

        if (!fileParameterMap.isEmpty()) {
            for (Map.Entry<String, List<FileParameter>> entry : fileParameterMap.entrySet()) {
                String name = entry.getKey();
                List<FileParameter> list = entry.getValue();
                FileParameter[] values = list.toArray(new FileParameter[0]);
                requestAdapter.setFileParameter(name, values);
            }
        }
    }
1319116
@BasePortalJpaDao.PortalTransactional
    public void resetUserLayoutAllProfiles(final IPersonAttributes personAttributes) {
        final IPerson person = PersonFactory.createRestrictedPerson();
        person.setAttributes(personAttributes.getAttributes());
        // get the integer uid into the person object without creating any new person data
        int uid = userIdentityStore.getPortalUID(person, false);
        person.setID(uid);

        final Hashtable<Integer, UserProfile> map = userLayoutStore.getUserProfileList(person);
        for (UserProfile profile : map.values()) {
            resetUserLayoutForProfileByName(person, profile);
            resetStylesheetUserPreferencesForProfile(person, profile);
        }
    }
611827
@Override
    public final Set<IPersonAttributes> mergeResults(final Set<IPersonAttributes> toModify, final Set<IPersonAttributes> toConsider) {
        Validate.notNull(toModify, "toModify cannot be null");
        Validate.notNull(toConsider, "toConsider cannot be null");

        //Convert the toModify Set into a Map to allow for easier lookups
        final Map<String, IPersonAttributes> toModfyPeople = new LinkedHashMap<>();
        for (final IPersonAttributes toModifyPerson : toModify) {
            toModfyPeople.put(toModifyPerson.getName(), toModifyPerson);
        }

        //Merge in the toConsider people
        for (final IPersonAttributes toConsiderPerson : toConsider) {
            final String toConsiderName = toConsiderPerson.getName();
            final IPersonAttributes toModifyPerson = toModfyPeople.get(toConsiderName);

            //No matching toModify person, just add the new person
            if (toModifyPerson == null) {
                toModify.add(toConsiderPerson);
            }
            //Matching toModify person, merge their attributes
            else {
                final Map<String, List<Object>> toModifyAttributes = this.buildMutableAttributeMap(toModifyPerson.getAttributes());
                final Map<String, List<Object>> mergedAttributes = this.mergePersonAttributes(toModifyAttributes, toConsiderPerson.getAttributes());
                final NamedPersonImpl mergedPerson = new NamedPersonImpl(toConsiderName, mergedAttributes);

                //Remove then re-add the mergedPerson entry
                toModify.remove(mergedPerson);
                toModify.add(mergedPerson);
            }
        }

        return toModify;
    }
1146259
@Override
    public List<IPersonAttributes> searchForPeople(
            final IPerson searcher, final Map<String, Object> query) {

        // get the IAuthorizationPrincipal for the searching user
        final IAuthorizationPrincipal principal = getPrincipalForUser(searcher);

        // build a set of all possible user attributes the current user has
        // permission to view
        final Set<String> permittedAttributes = getPermittedAttributes(principal);

        // remove any query attributes that the user does not have permission
        // to view
        final Map<String, Object> inUseQuery = new HashMap<>();
        for (Map.Entry<String, Object> queryEntry : query.entrySet()) {
            final String attr = queryEntry.getKey();
            if (permittedAttributes.contains(attr)) {
                inUseQuery.put(attr, queryEntry.getValue());
            } else {
                this.logger.warn(
                        "User '"
                                + searcher.getName()
                                + "' attempted searching on attribute '"
                                + attr
                                + "' which is not allowed in the current configuration. The attribute will be ignored.");
            }
        }

        // ensure the query has at least one search attribute defined
        if (inUseQuery.keySet().size() == 0) {
            throw new IllegalArgumentException("Search query is empty");
        }

        // get the set of people matching the search query
        final Set<IPersonAttributes> people = this.personAttributeDao.getPeople(inUseQuery);
        if (people == null) {
            return Collections.emptyList();
        }

        // To improve efficiency and not do as many permission checks or person directory searches,
        // if we have too many results and all people in the returned set of personAttributes have
        // a displayName, pre-sort the set and limit it to maxResults. The typical use case is that
        // LDAP returns results that have the displayName populated.  Note that a disadvantage of
        // this
        // approach is that the smaller result set may have entries that permissions prevent the
        // current users from viewing the person and thus reduce the number of final results, but
        // that is rare (typical use case is users can't view administrative internal accounts or
        // the
        // system account, none of which tend to be in LDAP).  We could retain a few more than
        // maxResults
        // to offset that chance, but IMHO not worth the cost of extra external queries.

        List<IPersonAttributes> peopleList = new ArrayList<>(people);
        if (peopleList.size() > maxResults && allListItemsHaveDisplayName(peopleList)) {
            logger.debug(
                    "All items contained displayName; pre-sorting list of size {} and truncating to {}",
                    peopleList.size(),
                    maxResults);
            // sort the list by display name
            Collections.sort(peopleList, new DisplayNameComparator());
            peopleList = peopleList.subList(0, maxResults);
        }
        // Construct a new representation of the persons limited to attributes the searcher
        // has permissions to view.  Will change order of the list.
        List<IPersonAttributes> list =
                getVisiblePersons(principal, permittedAttributes, peopleList);

        // Sort the list by display name
        Collections.sort(list, new DisplayNameComparator());

        // limit the list to a maximum number of returned results
        if (list.size() > maxResults) {
            list = list.subList(0, maxResults);
        }

        return list;
    }
937233
protected Set<String> facebookFieldNamesWithMultipleMappings(
      List<FieldWithAnnotation<Facebook>> fieldsWithAnnotation) {
    Map<String, Integer> facebookFieldsNamesWithOccurrenceCount = new HashMap<>();
    Set<String> facebookFieldNamesWithMultipleMappings = new HashSet<>();

    // Get a count of Facebook field name occurrences for each
    // @Facebook-annotated field
    for (FieldWithAnnotation<Facebook> fieldWithAnnotation : fieldsWithAnnotation) {
      String fieldName = getFacebookFieldName(fieldWithAnnotation);
      int occurrenceCount = facebookFieldsNamesWithOccurrenceCount.containsKey(fieldName)
          ? facebookFieldsNamesWithOccurrenceCount.get(fieldName)
          : 0;
      facebookFieldsNamesWithOccurrenceCount.put(fieldName, occurrenceCount + 1);
    }

    // Pull out only those field names with multiple mappings
    for (Entry<String, Integer> entry : facebookFieldsNamesWithOccurrenceCount.entrySet()) {
      if (entry.getValue() > 1) {
        facebookFieldNamesWithMultipleMappings.add(entry.getKey());
      }
    }

    return unmodifiableSet(facebookFieldNamesWithMultipleMappings);
  }
353797
@Override
    public Object[] getAttributeValues(String key) {
        if (userAttributes == null) {
            return null;
        }

        final List<Object> values = userAttributes.get(key);
        if (values != null) {
            return values.toArray();
        }

        return null;
    }
1041261
@Override
    protected List<IPersonAttributes> parseAttributeMapFromResults(final List<Map<String, Object>> queryResults, final String queryUserName) {
        final List<IPersonAttributes> peopleAttributes = new ArrayList<>(queryResults.size());

        for (final Map<String, Object> queryResult : queryResults) {
            final Map<String, List<Object>> multivaluedQueryResult = MultivaluedPersonAttributeUtils.toMultivaluedMap(queryResult);

            final IPersonAttributes person;
            final String userNameAttribute = this.getConfiguredUserNameAttribute();
            if (this.isUserNameAttributeConfigured() && queryResult.containsKey(userNameAttribute)) {
                // Option #1:  An attribute is named explicitly in the config, 
                // and that attribute is present in the results from JDBC;  use it
                person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, multivaluedQueryResult);
            } else if (queryUserName != null) {
                // Option #2:  Use the userName attribute provided in the query 
                // parameters.  (NB:  I'm not entirely sure this choice is 
                // preferable to Option #3.  Keeping it because it most closely 
                // matches the legacy behavior there the new option -- Option #1 
                // -- doesn't apply.  ~drewwills)
                person = new CaseInsensitiveNamedPersonImpl(queryUserName, multivaluedQueryResult);
            } else {
                // Option #3:  Create the IPersonAttributes doing a best-guess 
                // at a userName attribute
                person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, multivaluedQueryResult);
            }

            peopleAttributes.add(person);
        }

        return peopleAttributes;
    }
1194765
private Object[] getPersonGroupMemberKeys(IGroupMember gm) {
        Object[] keys = null;
        EntityIdentifier ei = gm.getUnderlyingEntityIdentifier();
        IPersonAttributes attr = personAttributeDao.getPerson(ei.getKey());
        if (attr != null && attr.getAttributes() != null && !attr.getAttributes().isEmpty()) {
            IPerson p = PersonFactory.createPerson();
            p.setAttributes(attr.getAttributes());
            keys = p.getAttributeValues(memberOfAttributeName);
            log.debug("Groups for person {} is: {}", p.getUserName(), Arrays.toString(keys));
        }
        return keys != null ? keys : new Object[] {};
    }
204148
@Override
    protected List<IPersonAttributes> getPeopleForQuery(final LogicalFilterWrapper queryBuilder, final String queryUserName) {
        final String generatedLdapQuery = queryBuilder.encode();

        //If no query is generated return null since the query cannot be run
        if (StringUtils.isBlank(generatedLdapQuery)) {
            return null;
        }

        //Insert the generated query into the template if it is configured
        final String ldapQuery;
        if (this.queryTemplate == null) {
            ldapQuery = generatedLdapQuery;
        } else {
            final Matcher queryMatcher = QUERY_PLACEHOLDER.matcher(this.queryTemplate);
            ldapQuery = queryMatcher.replaceAll(generatedLdapQuery);
            if (logger.isDebugEnabled()) {
                logger.debug("Final ldapQuery after applying queryTemplate: '" + ldapQuery + "'");
            }
        }

        //Execute the query
        @SuppressWarnings("unchecked")
        final List<Map<String, List<Object>>> queryResults = this.ldapTemplate.search(this.baseDN, ldapQuery, this.searchControls, MAPPER);

        final List<IPersonAttributes> peopleAttributes = new ArrayList<>(queryResults.size());
        for (final Map<String, List<Object>> queryResult : queryResults) {
            final IPersonAttributes person;

            // Choose a username from the best available option
            final String userNameAttribute = this.getConfiguredUserNameAttribute();
            if (this.isUserNameAttributeConfigured() && queryResult.containsKey(userNameAttribute)) {
                // Option #1:  An attribute is named explicitly in the config, 
                // and that attribute is present in the results from LDAP;  use it
                person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, queryResult);
            } else if (queryUserName != null) {
                // Option #2:  Use the userName attribute provided in the query 
                // parameters.  (NB:  I'm not entirely sure this choice is 
                // preferable to Option #3.  Keeping it because it most closely 
                // matches the legacy behavior there the new option -- Option #1 
                // -- doesn't apply.  ~drewwills)
                person = new CaseInsensitiveNamedPersonImpl(queryUserName, queryResult);
            } else {
                // Option #3:  Create the IPersonAttributes doing a best-guess 
                // at a userName attribute
                person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, queryResult);
            }

            peopleAttributes.add(person);
        }

        return peopleAttributes;
    }
1063354
@SneakyThrows
    public static IPersonAttributeDao newStubAttributeRepository(final PrincipalAttributesProperties p) {
        val dao = new NamedStubPersonAttributeDao();
        val pdirMap = new LinkedHashMap<String, List<Object>>();
        val stub = p.getStub();
        stub.getAttributes().forEach((key, value) -> {
            val vals = StringUtils.commaDelimitedListToStringArray(value);
            pdirMap.put(key, Arrays.stream(vals).collect(Collectors.toList()));
        });
        dao.setBackingMap(pdirMap);
        if (StringUtils.hasText(stub.getId())) {
            dao.setId(stub.getId());
        }
        return dao;
    }
1279921
@SuppressWarnings("unchecked")
  public static <K, V> MultiValueMap<K, V> unmodifiableMultiValueMap(
      final MultiValueMap<? extends K, ? extends V> map) {
    Assert.notNull(map, "'map' must not be null");
    final Map<K, List<V>> result = new LinkedHashMap<>(map.size());
    map.forEach((key, value) -> {
      final List<? extends V> values = Collections.unmodifiableList(value);
      result.put(key, (List<V>) values);
    });
    final Map<K, List<V>> unmodifiableMap = Collections.unmodifiableMap(result);
    return CollectionUtils.toMultiValueMap(unmodifiableMap);
  }
176003
@Override
    protected Map<String, List<Object>> mergePersonAttributes(final Map<String, List<Object>> toModify, final Map<String, List<Object>> toConsider) {
        Validate.notNull(toModify, "toModify cannot be null");
        Validate.notNull(toConsider, "toConsider cannot be null");

        toModify.putAll(toConsider);

        return toModify;
    }
918909
protected Map<String, List<Object>> extractPersonAttributes(final X509Certificate certificate) {
        val attributes = new LinkedHashMap<String, List<Object>>();

        if (certificate != null) {
            if (StringUtils.isNotBlank(certificate.getSigAlgOID())) {
                attributes.put("sigAlgOid", CollectionUtils.wrapList(certificate.getSigAlgOID()));
            }
            val subjectDn = certificate.getSubjectDN();
            if (subjectDn != null) {
                attributes.put("subjectDn", CollectionUtils.wrapList(subjectDn.getName()));
            }
            val subjectPrincipal = certificate.getSubjectX500Principal();
            if (subjectPrincipal != null) {
                attributes.put("subjectX500Principal", CollectionUtils.wrapList(subjectPrincipal.getName()));
            }
            try {
                val rfc822Email = getRFC822EmailAddress(certificate.getSubjectAlternativeNames());
                if (rfc822Email != null) {
                    attributes.put("x509Rfc822Email", CollectionUtils.wrapList(rfc822Email));
                }
            } catch (final CertificateParsingException e) {
                LOGGER.warn("Error parsing subject alternative names to get rfc822 email [{}]", e.getMessage());
            }
        }
        return attributes;
    }
290865
public List<String> getPeople()
    {
        try ( Session session = driver.session() )
        {
            return session.readTransaction( new TransactionWork<List<String>>()
            {
                @Override
                public List<String> execute( Transaction tx )
                {
                    return matchPersonNodes( tx );
                }
            } );
        }
    }
488593
@Override
    public Set<String> getDisplayAttributes(ExternalContext externalContext) {
        final PortletRequest portletRequest = (PortletRequest) externalContext.getNativeRequest();
        final PortletPreferences preferences = portletRequest.getPreferences();

        final Set<String> displayAttributes;
        final String[] configuredAttributes =
                preferences.getValues(PERSON_LOOKUP_PERSON_DETAILS_DETAILS_ATTRIBUTES, null);
        final String[] excludedAttributes =
                preferences.getValues(
                        PERSON_LOOKUP_PERSON_DETAILS_DETAILS_ATTRIBUTES_EXCLUDES, null);

        // If attributes are configured in portlet prefs use those the user has
        if (configuredAttributes != null) {
            displayAttributes = new LinkedHashSet<>();
            displayAttributes.addAll(Arrays.asList(configuredAttributes));
        }
        // Otherwise provide all available attributes from the IPersonAttributes
        else {
            displayAttributes = new TreeSet<>(personAttributeDao.getPossibleUserAttributeNames());
        }

        // Remove any excluded attributes
        if (excludedAttributes != null) {
            for (final String excludedAttribute : excludedAttributes) {
                displayAttributes.remove(excludedAttribute);
            }
        }

        return displayAttributes;
    }