Java tutorial
/************************************************************************* * (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. ************************************************************************/ package com.eucalyptus.compute.common.internal.account; import static com.eucalyptus.auth.policy.PolicySpec.IAM_RESOURCE_ROLE; import static com.eucalyptus.auth.policy.PolicySpec.IAM_RESOURCE_USER; import static com.eucalyptus.auth.policy.PolicySpec.VENDOR_IAM; import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.eucalyptus.auth.policy.PolicySpec; import com.eucalyptus.auth.policy.ern.Ern; import com.eucalyptus.compute.common.IdFormatItemType; import com.eucalyptus.compute.common.internal.account.IdentityIdFormat.IdResource; import com.eucalyptus.compute.common.internal.account.IdentityIdFormat.IdType; import com.eucalyptus.compute.common.internal.identifier.ResourceIdentifiers; import com.eucalyptus.entities.Entities; import com.eucalyptus.entities.EntityRestriction; import com.eucalyptus.entities.TransactionResource; import com.eucalyptus.util.Pair; import com.eucalyptus.util.TypeMapper; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; /** * */ @SuppressWarnings({ "Guava", "StaticPseudoFunctionalStyleMethod", "OptionalUsedAsFieldOrParameterType" }) public class IdentityIdFormats { private static final Set<String> LONG_ID_RESOURCES = ImmutableSet .copyOf(Stream.of(IdResource.values()).map(IdResource::name).iterator()); private static final Map<String, String> LONG_ID_PREFIX_TO_RESOURCE = ImmutableMap .copyOf(Stream.of(IdResource.values()).collect(Collectors.toMap(IdResource::prefix, IdResource::name))); private static final Map<String, String> LONG_ID_RESOURCE_TO_PREFIX = ImmutableMap .copyOf(Stream.of(IdResource.values()).collect(Collectors.toMap(IdResource::name, IdResource::prefix))); private static final LoadingCache<Pair<String, String>, Optional<Boolean>> LONG_ID_CONFIG_CACHE = CacheBuilder .newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).maximumSize(1_000) .build(new CacheLoader<Pair<String, String>, Optional<Boolean>>() { @Override public Optional<Boolean> load(@Nonnull final Pair<String, String> identityArnAndResource) { final String identityArn = identityArnAndResource.getLeft(); final String resource = identityArnAndResource.getRight(); final IdResource idResource = IdResource.valueOf(resource); final Optional<Pair<String, Pair<IdType, String>>> identityOption = IdentityIdFormats .tryParseIdentity(identityArn); if (identityOption.isPresent()) { final Optional<Boolean> identityLongIds = loadIdFormat(identityOption.get().getLeft(), identityOption.get().getRight().getLeft(), identityOption.get().getRight().getRight(), idResource); final Optional<Boolean> accountLongIds = identityOption.get().getRight() .getLeft() == IdType.account ? identityLongIds : loadIdFormat(identityOption.get().getLeft(), IdType.account, identityOption.get().getLeft(), idResource); return identityLongIds.or(accountLongIds); } return Optional.absent(); } }); public static String generate(@Nonnull final String identityArn, @Nonnull final String prefix) { final String resource = LONG_ID_PREFIX_TO_RESOURCE.get(prefix); if (resource != null && ResourceIdentifiers.useAccountLongIdentifierSettings()) { final Optional<Boolean> configuredLongIds = LONG_ID_CONFIG_CACHE .getUnchecked(Pair.pair(identityArn, resource)); if (configuredLongIds.isPresent()) { if (configuredLongIds.get()) { return ResourceIdentifiers.generateLongString(prefix); } else { return ResourceIdentifiers.generateShortString(prefix); } } } return ResourceIdentifiers.generateString(prefix); } public static boolean isValidResource(final String resource) { return LONG_ID_RESOURCES.contains(resource); } @SuppressWarnings("WeakerAccess") public static Optional<Pair<String, Pair<IdType, String>>> tryParseIdentity(final String identityArn) { return tryParseIdentity(null, identityArn); } public static Optional<Pair<String, Pair<IdType, String>>> tryParseIdentity(final String accountNumber, final String identityArn) { final Matcher accountRootArnMatcher = Pattern.compile("arn:aws:iam::([0-9]{12}):root").matcher(identityArn); if (accountRootArnMatcher.matches() && (accountNumber == null || accountNumber.equals(accountRootArnMatcher.group(1)))) { return Optional.of(Pair.pair(accountRootArnMatcher.group(1), Pair.pair(IdType.account, accountRootArnMatcher.group(1)))); } try { final Ern ern = Ern.parse(identityArn); if (VENDOR_IAM.equals(ern.getService()) && ern.getAccount() != null && (accountNumber == null || accountNumber.equals(ern.getAccount()))) { if (PolicySpec.qualifiedName(VENDOR_IAM, IAM_RESOURCE_ROLE).equals(ern.getResourceType())) { return Optional.of(Pair.pair(ern.getAccount(), Pair.pair(IdType.role, ern.getResourceName()))); } else if (PolicySpec.qualifiedName(VENDOR_IAM, IAM_RESOURCE_USER).equals(ern.getResourceType())) { return Optional.of(Pair.pair(ern.getAccount(), Pair.pair(IdType.user, ern.getResourceName()))); } } } catch (Exception e) { // not a valid user/role ARN } return Optional.absent(); } @SuppressWarnings("WeakerAccess") public static Optional<Boolean> loadIdFormat(final String accountNumber, final IdType type, final String id, final IdResource resource) { //noinspection unused try (final TransactionResource tx = Entities.transactionFor(IdentityIdFormat.class)) { final IdentityIdFormat idFormat = Entities.criteriaQuery(IdentityIdFormat.class) .whereEqual(IdentityIdFormat_.accountNumber, accountNumber) .whereEqual(IdentityIdFormat_.identityType, type) .whereEqual(IdentityIdFormat_.identityFullName, id) .whereEqual(IdentityIdFormat_.resource, resource).uniqueResult(); return Optional.of(idFormat.getUseLongIdentifiers()); } catch (final NoSuchElementException e) { return Optional.absent(); } } public static boolean saveIdFormat(final String accountNumber, final IdType type, final String id, final IdResource resource, final Boolean useLongIds) { try { Entities.asDistinctTransaction(IdentityIdFormat.class, new Function<Void, IdentityIdFormat>() { @Nullable @Override public IdentityIdFormat apply(@Nullable final Void aVoid) { final IdentityIdFormat idFormat = Entities.criteriaQuery(IdentityIdFormat.class) .whereEqual(IdentityIdFormat_.accountNumber, accountNumber) .whereEqual(IdentityIdFormat_.identityType, type) .whereEqual(IdentityIdFormat_.identityFullName, id) .whereEqual(IdentityIdFormat_.resource, resource).uniqueResult(); idFormat.setUseLongIdentifiers(useLongIds); return idFormat; } }).apply(null); } catch (NoSuchElementException e) { // so create try (final TransactionResource tx = Entities.transactionFor(IdentityIdFormat.class)) { Entities.persist(IdentityIdFormat.create(accountNumber, type, id, resource, useLongIds)); tx.commit(); } } return true; } @SuppressWarnings("WeakerAccess") public static List<IdentityIdFormat> listIdFormats(final String accountNumber, final IdType type, final String id, final Optional<IdResource> resource) { //noinspection unused try (final TransactionResource tx = Entities.transactionFor(IdentityIdFormat.class)) { final EntityRestriction<IdentityIdFormat> resourceRestriction = resource.isPresent() ? Entities.restriction(IdentityIdFormat.class).equal(IdentityIdFormat_.resource, resource.get()) .build() : Entities.restriction(IdentityIdFormat.class).build(); return Entities.criteriaQuery(IdentityIdFormat.class) .whereEqual(IdentityIdFormat_.accountNumber, accountNumber) .whereEqual(IdentityIdFormat_.identityType, type) .whereEqual(IdentityIdFormat_.identityFullName, id).where(resourceRestriction).readonly() .list(); } } public static List<IdentityIdFormat> listIdFormatsWithDefaults(final String accountNumber, final IdType type, final String id, final Optional<IdResource> resource) { final Set<IdResource> desiredResources = resource.isPresent() ? resource.asSet() : EnumSet.allOf(IdResource.class); //noinspection unused try (final TransactionResource tx = Entities.transactionFor(IdentityIdFormat.class)) { final List<IdentityIdFormat> formats = Lists .newArrayList(listIdFormats(accountNumber, type, id, resource)); final Set<IdResource> idSpecificResources = formats.stream().map(IdentityIdFormat::getResource) .collect(Collectors.toSet()); if (type != IdType.account && !idSpecificResources.containsAll(desiredResources)) { formats.addAll(listIdFormats(accountNumber, IdType.account, accountNumber, resource).stream() .filter(accountFormat -> !idSpecificResources.contains(accountFormat.getResource())) .collect(Collectors.toList())); } final Set<IdResource> configuredResources = formats.stream().map(IdentityIdFormat::getResource) .collect(Collectors.toSet()); formats.addAll(desiredResources.stream() .filter(configurableResource -> !configuredResources.contains(configurableResource)) .map(configurableResource -> IdentityIdFormat.create(accountNumber, IdType.account, accountNumber, configurableResource, ResourceIdentifiers.useLongIdentifierForPrefix( LONG_ID_RESOURCE_TO_PREFIX.get(configurableResource.name())))) .collect(Collectors.toList())); formats.sort(Comparator.comparing(IdentityIdFormat::getResource)); return formats; } } @TypeMapper public enum IdentityIdFormatToIdFormatItemType implements Function<IdentityIdFormat, IdFormatItemType> { INSTANCE; @Nullable @Override public IdFormatItemType apply(@Nullable final IdentityIdFormat format) { return format == null ? null : new IdFormatItemType(Objects.toString(format.getResource()), format.getUseLongIdentifiers()); } } }