/*
* The MIT License
*
* Copyright (c) 2011-2013, CloudBees, Inc., Stephen Connolly.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.cloudbees.plugins.credentials.domains;
import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.util.CopyOnWriteMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* Represents a {@link Domain} and an associated set of {@link Credentials}.
*
* @since 1.5
*/
public class DomainCredentials {
/**
* The domain that these credentials are scoped to.
*/
@NonNull
private final Domain domain;
/**
* The credentials scoped to this domain.
*/
@NonNull
private final List<Credentials> credentials;
/**
* Stapler's constructor.
*
* @param domain the domain.
* @param credentials the credentials.
*/
@DataBoundConstructor
public DomainCredentials(Domain domain, List<Credentials> credentials) {
this.domain = domain == null ? Domain.global() : domain.resolve();
this.credentials = credentials == null ? new ArrayList<Credentials>() : new ArrayList<Credentials>(credentials);
}
/**
* Converts a {@link Collection} of {@link DomainCredentials} into a {@link Map} keyed by {@link Domain} with
* {@link List} of {@link Credentials} as values.
*
* @param collection the collection.
* @return the corresponding map.
*/
@NonNull
public static Map<Domain, List<Credentials>> asMap(@CheckForNull Collection<DomainCredentials> collection) {
Map<Domain, List<Credentials>> map = new LinkedHashMap<Domain, List<Credentials>>();
if (collection != null) {
for (DomainCredentials item : collection) {
List<Credentials> existing = map.get(item.getDomain());
if (existing == null) {
map.put(item.getDomain(), new CopyOnWriteArrayList<Credentials>(item.getCredentials()));
} else {
// allow combining for malformed requests
existing.addAll(item.getCredentials());
}
}
}
return new CopyOnWriteMap.Hash<Domain, List<Credentials>>(map);
}
/**
* Converts a {@link Map} keyed by {@link Domain} with {@link List} of {@link Credentials} as values into a
* {@link List} of {@link DomainCredentials} into a
*
* @param map the map.
* @return the corresponding list.
*/
@NonNull
public static List<DomainCredentials> asList(Map<Domain, List<Credentials>> map) {
List<DomainCredentials> result = new ArrayList<DomainCredentials>();
if (map != null) {
for (Map.Entry<Domain, List<Credentials>> entry : map.entrySet()) {
result.add(new DomainCredentials(entry.getKey(), entry.getValue()));
}
}
return result;
}
/**
* Converts a {@link Map} keyed by {@link Domain} with {@link List} of {@link Credentials} as values into a
* {@link List} of {@link DomainCredentials} into a
*
* @param map the map.
* @return the corresponding list.
*/
@NonNull
public static Map<Domain, List<Credentials>> toCopyOnWriteMap(@CheckForNull Map<Domain, List<Credentials>> map) {
if (map instanceof CopyOnWriteMap.Hash) {
// if we get this far, likely we will find all entries as CopyOnWriteArrayList
// so we should almost always be returning after one iteration.
boolean allCopyOnWrite = true;
for (List<Credentials> list : map.values()) {
if (!(list instanceof CopyOnWriteArrayList)) {
allCopyOnWrite = false;
break;
}
}
if (allCopyOnWrite) {
return map;
}
}
Map<Domain, List<Credentials>> tmp = new LinkedHashMap<Domain, List<Credentials>>();
if (map != null) {
for (Map.Entry<Domain, List<Credentials>> entry : map.entrySet()) {
tmp.put(entry.getKey() == null
? Domain.global()
: entry.getKey().resolve(),
new CopyOnWriteArrayList<Credentials>(
entry.getValue() == null
? Collections.<Credentials>emptyList()
: entry.getValue()));
}
}
return new CopyOnWriteMap.Hash<Domain, List<Credentials>>(tmp);
}
/**
* Handle migration of standard storage method for pre-domain data into domain segmented data.
*
* @param map the new map based store.
* @param list the old list based store.
* @return consolidated map based store.
*/
public static Map<Domain, List<Credentials>> migrateListToMap(@CheckForNull Map<Domain, List<Credentials>> map,
@CheckForNull List<Credentials> list) {
if (map == null) {
map = new CopyOnWriteMap.Hash<Domain, List<Credentials>>();
}
if (!map.containsKey(Domain.global())) {
if (list == null) {
map.put(Domain.global(), new CopyOnWriteArrayList<Credentials>());
} else {
map.put(Domain.global(), new CopyOnWriteArrayList<Credentials>(list));
}
}
return map;
}
/**
* Helper to assist retrieving credentials from the map based store.
*
* @param domainCredentialsMap map of credentials by domain.
* @param type type of credential to retrieve.
* @param domainRequirements domain requirements.
* @param credentialsMatcher what subset of credentials to match.
* @param <C> the type of credential to retrieve.
* @return a {@link List} of matching credentials.
*/
@NonNull
public static <C extends Credentials> List<C> getCredentials(
@NonNull Map<Domain, List<Credentials>> domainCredentialsMap,
@NonNull Class<C> type,
@NonNull List<DomainRequirement> domainRequirements,
@NonNull CredentialsMatcher credentialsMatcher) {
List<C> result = new ArrayList<C>();
for (Map.Entry<Domain, List<Credentials>> entry : domainCredentialsMap.entrySet()) {
if (entry.getKey().test(domainRequirements)) {
for (Credentials credential : entry.getValue()) {
if (!type.isInstance(credential)) {
continue;
}
// If the credentials have a native restriction that isn't imposed
// by the Domain, give the Credentials a chance to self-restrict
// themselves from being surfaced.
if (credential instanceof DomainRestrictedCredentials
&& !((DomainRestrictedCredentials) credential).matches(domainRequirements)) {
continue;
}
if (credentialsMatcher.matches(credential)) {
result.add(type.cast(credential));
}
}
}
}
return result;
}
/**
* Helper method used by the {@code domainCredentials.jelly} taglib to ensure the list is valid.
*
* @param list the list.
* @return the list with fixes applied.
*/
@NonNull
public static List<DomainCredentials> fixList(@CheckForNull List<DomainCredentials> list) {
Map<Domain, List<Credentials>> map = asMap(list);
if (!map.containsKey(Domain.global())) {
map.put(Domain.global(), new CopyOnWriteArrayList<Credentials>());
}
return asList(map);
}
/**
* Returns the domain.
*
* @return the domain.
*/
@NonNull
@SuppressWarnings("unused") // by stapler
public Domain getDomain() {
return domain;
}
/**
* Returns the credentials.
*
* @return the credentials.
*/
@NonNull
@SuppressWarnings("unused") // by stapler
public List<Credentials> getCredentials() {
return credentials;
}
}