/************************************************************************* * (c) Copyright 2017 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.tags; import java.util.List; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.eucalyptus.auth.AuthContextSupplier; import com.eucalyptus.auth.Permissions; import com.eucalyptus.auth.policy.PolicySpec; import com.eucalyptus.auth.principal.AccountFullName; import com.eucalyptus.auth.principal.UserFullName; import com.eucalyptus.compute.common.CloudMetadata; import com.eucalyptus.compute.common.ResourceTag; import com.eucalyptus.compute.common.ResourceTagSpecification; import com.eucalyptus.compute.common.internal.tags.TagSupport; import com.eucalyptus.compute.common.internal.util.MetadataException; import com.eucalyptus.context.Context; import com.eucalyptus.context.Contexts; import com.eucalyptus.util.Assert; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; /** * */ public class TagHelper { private static final Set<String> reservedPrefixes = ImmutableSet.<String>builder().add("aws:").add("euca:").build(); /** * Check if the tag is reserved in the given context. * * @param tagName The tag name to check. * @return true if reserved (not permitted for C_UD) * @see Contexts#lookup */ public static boolean isReserved( final String tagName ) { return !Contexts.lookup( ).isPrivileged( ) && Iterables.any( reservedPrefixes, prefix( tagName ) ); } /** * Reserved tag key description */ @Nonnull public static String describeReserved( ) { return String.valueOf( reservedPrefixes ); } /** * Validate any key/value pairs present in the given specification for the current context. * * @param tagSpecifications The optional tag specification to validate * @throws MetadataException */ public static void validateTagSpecifications( @Nullable final List<ResourceTagSpecification> tagSpecifications ) throws MetadataException { if ( tagSpecifications != null ) { final Set<String> resourceTypes = Sets.newHashSet( ); for ( final ResourceTagSpecification tagSpecification : tagSpecifications ) { if ( tagSpecification.getResourceType( ) != null && !resourceTypes.add( tagSpecification.getResourceType( ) ) ) { throw new InvalidTagMetadataException( "The same resource type may not be specified more than once in tag specifications" ); } TagHelper.validateTags( tagSpecification.getTagSet( ) ); } } } /** * Validate the given list of tags for the current context. * * @param resourceTags * @throws MetadataException */ public static void validateTags( @Nullable final List<ResourceTag> resourceTags ) throws MetadataException { final Set<String> tagKeys = Sets.newHashSet( ); if ( resourceTags != null ) for ( final ResourceTag resourceTag : resourceTags ) { final String key = resourceTag.getKey(); final String value = Strings.nullToEmpty( resourceTag.getValue() ).trim(); if ( isReserved( key ) ) { throw new InvalidTagMetadataException( "Tag keys starting with 'aws:' and 'euca:' are reserved for internal use" ); } if ( Strings.isNullOrEmpty( key ) || key.trim().length() > 127 ) { throw new InvalidTagMetadataException( "Tag key exceeds the maximum length of 127 characters" ); } if ( value.length() > 255 ) { throw new InvalidTagMetadataException( "Tag value exceeds the maximum length of 255 characters" ); } if ( !tagKeys.add( key ) ) { throw new InvalidTagMetadataException( "Duplicate tag key" ); } } } /** * Get the tags for the specified resource. * * @param tagSpecifications The optional specifications * @param resource The resource type * @return tags for the resource type (may be empty) */ @Nonnull public static List<ResourceTag> tagsForResource( @Nullable final List<ResourceTagSpecification> tagSpecifications, @Nonnull final String resource ) { Assert.arg( PolicySpec.EC2_RESOURCES.contains( resource ), "Invalid EC2 resource: %1$s", resource ); final List<ResourceTag> tags = Lists.newArrayList( ); if ( tagSpecifications != null ) for( final ResourceTagSpecification tagSpecification : tagSpecifications ) { final List<ResourceTag> resourceTags = tagSpecification.getTagSet( ); if ( resourceTags != null && resource.equals( tagSpecification.getResourceType( ) ) ) { tags.addAll( resourceTags ); } } return tags; } /** * Caller must have open transaction for resources. * * Caller must check permissions. */ public static void createOrUpdateTags( @Nonnull final UserFullName userFullName, @Nonnull final CloudMetadata resource, @Nonnull final List<ResourceTag> resourceTags ) { createOrUpdateTags( userFullName, Lists.newArrayList( resource ), resourceTags ); } /** * Caller must have open transaction for resources. * * Caller must check permissions. */ public static void createOrUpdateTags( @Nonnull final UserFullName userFullName, @Nonnull final List<CloudMetadata> resources, @Nonnull final List<ResourceTag> resourceTags ) { for ( final CloudMetadata resource : resources ) { for ( final ResourceTag resourceTag : resourceTags ) { final String key = Strings.nullToEmpty( resourceTag.getKey() ).trim(); final String value = Strings.nullToEmpty( resourceTag.getValue() ).trim(); TagSupport.fromResource( resource ).createOrUpdate( resource, userFullName, key, value ); } if ( TagSupport.fromResource( resource ).count( resource, userFullName.asAccountFullName( ) ) > TagManager.MAX_TAGS_PER_RESOURCE ) { throw new TagLimitException(); } } } public static boolean createTagsAuthorized( @Nonnull final Context ctx, @Nonnull final String resourceType ) { return createTagsAuthorized( ctx.getAuthContext( ), ctx.getAccount( ), resourceType ); } public static boolean createTagsAuthorized( @Nonnull final AuthContextSupplier authContext, @Nonnull final AccountFullName accountFullName, @Nonnull final String resourceType ) { return Permissions.isAuthorized( PolicySpec.VENDOR_EC2, resourceType, "", accountFullName, PolicySpec.EC2_CREATETAGS, authContext ); } private static Predicate<String> prefix( final String text ) { return new Predicate<String>() { @Override public boolean apply( final String prefix ) { return text != null && text.trim().startsWith( prefix ); } }; } }