Skip to content

Commit

Permalink
Invert how harmonized API is mounted #10624
Browse files Browse the repository at this point in the history
  • Loading branch information
anatol-sialitski committed Aug 15, 2024
1 parent 492fe58 commit dcefb15
Show file tree
Hide file tree
Showing 39 changed files with 1,279 additions and 586 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.enonic.xp.admin.tool;

import java.util.Objects;

import com.google.common.collect.ImmutableSet;

import com.enonic.xp.annotation.PublicApi;
import com.enonic.xp.api.ApiMountDescriptors;
import com.enonic.xp.app.ApplicationKey;
import com.enonic.xp.page.DescriptorKey;
import com.enonic.xp.resource.ResourceKey;
Expand All @@ -25,6 +28,8 @@ public class AdminToolDescriptor

private final PrincipalKeys allowedPrincipals;

private final ApiMountDescriptors apiMounts;

private AdminToolDescriptor( final Builder builder )
{
key = builder.key;
Expand All @@ -33,6 +38,7 @@ private AdminToolDescriptor( final Builder builder )
description = builder.description;
descriptionI18nKey = builder.descriptionI18nKey;
allowedPrincipals = PrincipalKeys.from( builder.allowedPrincipals.build() );
apiMounts = Objects.requireNonNullElse( builder.apiMounts, ApiMountDescriptors.empty() );
}

public DescriptorKey getKey()
Expand Down Expand Up @@ -77,15 +83,19 @@ public PrincipalKeys getAllowedPrincipals()

public boolean isAccessAllowed( final PrincipalKeys principalKeys )
{
return principalKeys.contains( RoleKeys.ADMIN ) || principalKeys.stream().
anyMatch( allowedPrincipals::contains );
return principalKeys.contains( RoleKeys.ADMIN ) || principalKeys.stream().anyMatch( allowedPrincipals::contains );
}

public boolean isAppLauncherApplication()
{
return displayName != null;
}

public ApiMountDescriptors getApiMounts()
{
return apiMounts;
}

public static ResourceKey toResourceKey( final DescriptorKey key )
{
return ResourceKey.from( key.getApplicationKey(), "admin/tools/" + key.getName() + "/" + key.getName() + ".xml" );
Expand Down Expand Up @@ -114,6 +124,8 @@ public static final class Builder

private String descriptionI18nKey;

private ApiMountDescriptors apiMounts;

private final ImmutableSet.Builder<PrincipalKey> allowedPrincipals = ImmutableSet.builder();

private Builder()
Expand Down Expand Up @@ -156,6 +168,12 @@ public Builder addAllowedPrincipals( final PrincipalKey allowedPrincipal )
return this;
}

public Builder apiMounts( final ApiMountDescriptors apiMountDescriptors )
{
this.apiMounts = apiMountDescriptors;
return this;
}

public AdminToolDescriptor build()
{
return new AdminToolDescriptor( this );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ private void parseXml( final Resource resource, final AdminToolDescriptor.Builde
try
{
final XmlAdminToolDescriptorParser parser = new XmlAdminToolDescriptorParser();
parser.currentApplication( resource.getKey().getApplicationKey() );
parser.builder( builder );
parser.source( resource.readString() );
parser.parse();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
package com.enonic.xp.admin.impl.tool;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import com.enonic.xp.admin.tool.AdminToolDescriptor;
import com.enonic.xp.api.ApiMountDescriptor;
import com.enonic.xp.api.ApiMountDescriptors;
import com.enonic.xp.app.ApplicationKey;
import com.enonic.xp.security.PrincipalKey;
import com.enonic.xp.xml.DomElement;
import com.enonic.xp.xml.parser.XmlModelParser;

public class XmlAdminToolDescriptorParser
extends XmlModelParser<XmlAdminToolDescriptorParser>
{
private static final String APIS_DESCRIPTOR_TAG_NAME = "apis";

private static final String API_DESCRIPTOR_TAG_NAME = "api";

private static final int APPLICATION_KEY_INDEX = 0;

private static final int API_KEY_INDEX = 1;

private AdminToolDescriptor.Builder builder;

public XmlAdminToolDescriptorParser builder( final AdminToolDescriptor.Builder builder )
Expand All @@ -31,7 +44,6 @@ protected void doParse( final DomElement root )
this.builder.descriptionI18nKey(
root.getChild( "description" ) != null ? root.getChild( "description" ).getAttribute( "i18n" ) : null );


final DomElement allowedPrincipals = root.getChild( "allow" );
if ( allowedPrincipals != null )
{
Expand All @@ -41,5 +53,58 @@ protected void doParse( final DomElement root )
this.builder.addAllowedPrincipals( PrincipalKey.from( allowedPrincipal.getValue().trim() ) );
}
}

this.builder.apiMounts( ApiMountDescriptors.from( parseApiMounts( root.getChild( APIS_DESCRIPTOR_TAG_NAME ) ) ) );
}

private List<ApiMountDescriptor> parseApiMounts( final DomElement apisElement )
{
if ( apisElement == null )
{
return Collections.emptyList();
}

return apisElement.getChildren( API_DESCRIPTOR_TAG_NAME ).stream().map( this::toApiMountDescriptor ).collect( Collectors.toList() );
}

private ApiMountDescriptor toApiMountDescriptor( final DomElement apiElement )
{
final ApiMountDescriptor.Builder builder = ApiMountDescriptor.create();

final String apiMount = apiElement.getValue().trim();

if ( !apiMount.contains( ":" ) )
{
builder.applicationKey( this.currentApplication );
if ( !apiMount.isBlank() )
{
builder.apiKey( apiMount );
}
}
else
{
final String[] parts = apiMount.split( ":", 2 );

builder.applicationKey( resolveApplicationKey( parts[APPLICATION_KEY_INDEX].trim() ) );
final String apiKey = parts[API_KEY_INDEX].trim();
if ( !apiKey.isBlank() )
{
builder.apiKey( apiKey );
}
}

return builder.build();
}

private ApplicationKey resolveApplicationKey( final String applicationKey )
{
try
{
return ApplicationKey.from( applicationKey );
}
catch ( Exception e )
{
throw new IllegalArgumentException( String.format( "Invalid applicationKey '%s'", applicationKey ), e );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
import org.junit.jupiter.api.Test;

import com.enonic.xp.admin.tool.AdminToolDescriptor;
import com.enonic.xp.api.ApiMountDescriptor;
import com.enonic.xp.app.ApplicationKey;
import com.enonic.xp.page.DescriptorKey;
import com.enonic.xp.security.PrincipalKey;
import com.enonic.xp.security.PrincipalKeys;
import com.enonic.xp.xml.XmlException;
import com.enonic.xp.xml.parser.XmlModelParserTest;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class XmlAdminToolDescriptorParserTest
extends XmlModelParserTest
Expand All @@ -37,30 +39,68 @@ public void testParse()
throws Exception
{
parse( this.parser, ".xml" );
assertResult();

final AdminToolDescriptor toolDescriptor = this.builder.build();
assertResult( toolDescriptor );
}

@Test
public void testParseWithApis()
throws Exception
{
parse( this.parser, "-apis.xml" );

final AdminToolDescriptor toolDescriptor = this.builder.build();

assertResult( toolDescriptor );

assertEquals( 4, toolDescriptor.getApiMounts().getSize() );

final ApiMountDescriptor apiMountDescriptor1 = toolDescriptor.getApiMounts().get( 0 );
assertEquals( ApplicationKey.from( "com.enonic.app.myapp" ), apiMountDescriptor1.getApplicationKey() );
assertEquals( "api-key", apiMountDescriptor1.getApiKey() );

final ApiMountDescriptor apiMountDescriptor2 = toolDescriptor.getApiMounts().get( 1 );
assertEquals( ApplicationKey.from( "com.enonic.app.myapp" ), apiMountDescriptor2.getApplicationKey() );
assertEquals( "api", apiMountDescriptor2.getApiKey() );

final ApiMountDescriptor apiMountDescriptor3 = toolDescriptor.getApiMounts().get( 2 );
assertEquals( ApplicationKey.from( "myapplication" ), apiMountDescriptor3.getApplicationKey() );
assertEquals( "api-key", apiMountDescriptor3.getApiKey() );

final ApiMountDescriptor apiMountDescriptor4 = toolDescriptor.getApiMounts().get( 3 );
assertEquals( ApplicationKey.from( "myapplication" ), apiMountDescriptor4.getApplicationKey() );
assertEquals( "api", apiMountDescriptor4.getApiKey() );
}

@Test
public void testParseWithInvalidApis()
{
XmlException ex = assertThrows( XmlException.class, () -> parse( this.parser, "-apis-invalid.xml" ) );
assertEquals( "Invalid applicationKey ''", ex.getMessage() );
}

@Test
public void testParse_noNs()
throws Exception
{
parseRemoveNs( this.parser, ".xml" );
assertResult();

final AdminToolDescriptor toolDescriptor = this.builder.build();
assertResult( toolDescriptor );
}

private void assertResult()
throws Exception
private void assertResult( final AdminToolDescriptor toolDescriptor )
{
final AdminToolDescriptor result = this.builder.build();
assertEquals( "myapplication:myadmintool", result.getKey().toString() );
assertEquals( "My admin tool", result.getDisplayName() );
assertEquals( "myapplication:myadmintool", toolDescriptor.getKey().toString() );
assertEquals( "My admin tool", toolDescriptor.getDisplayName() );

assertEquals( "key.display-name", result.getDisplayNameI18nKey() );
assertEquals( "key.description", result.getDescriptionI18nKey() );
assertEquals( "key.display-name", toolDescriptor.getDisplayNameI18nKey() );
assertEquals( "key.description", toolDescriptor.getDescriptionI18nKey() );

final PrincipalKeys allowedPrincipals = result.getAllowedPrincipals();
final PrincipalKeys allowedPrincipals = toolDescriptor.getAllowedPrincipals();
assertNotNull( allowedPrincipals );
assertEquals( 1, allowedPrincipals.getSize() );
assertTrue( allowedPrincipals.first().equals( PrincipalKey.from( "role:system.admin" ) ) );
assertEquals( allowedPrincipals.first(), PrincipalKey.from( "role:system.admin" ) );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<tool xmlns="urn:enonic:xp:model:1.0">
<display-name i18n="key.display-name">My admin tool</display-name>
<description i18n="key.description">Tool description</description>
<allow>
<principal>role:system.admin</principal>
</allow>
<apis>
<api>:api-key</api>
</apis>
</tool>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<tool xmlns="urn:enonic:xp:model:1.0">
<display-name i18n="key.display-name">My admin tool</display-name>
<description i18n="key.description">Tool description</description>
<allow>
<principal>role:system.admin</principal>
</allow>
<apis>
<api>com.enonic.app.myapp:api-key</api>
<api>com.enonic.app.myapp:</api>
<api>api-key</api>
<api/>
</apis>
</tool>

This file was deleted.

Loading

0 comments on commit dcefb15

Please sign in to comment.