Skip to content

Commit 28b822b

Browse files
Mount widget interfaces to admin tool #10701
1 parent 8e10f12 commit 28b822b

File tree

8 files changed

+119
-5
lines changed

8 files changed

+119
-5
lines changed

modules/admin/admin-api/src/main/java/com/enonic/xp/admin/tool/AdminToolDescriptor.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.enonic.xp.admin.tool;
22

33
import java.util.Objects;
4+
import java.util.Set;
45

56
import com.google.common.collect.ImmutableSet;
67

@@ -30,6 +31,8 @@ public class AdminToolDescriptor
3031

3132
private final ApiMountDescriptors apiMounts;
3233

34+
private final ImmutableSet<String> interfaces;
35+
3336
private AdminToolDescriptor( final Builder builder )
3437
{
3538
key = builder.key;
@@ -39,6 +42,7 @@ private AdminToolDescriptor( final Builder builder )
3942
descriptionI18nKey = builder.descriptionI18nKey;
4043
allowedPrincipals = PrincipalKeys.from( builder.allowedPrincipals.build() );
4144
apiMounts = Objects.requireNonNullElse( builder.apiMounts, ApiMountDescriptors.empty() );
45+
interfaces = builder.interfaces.build();
4246
}
4347

4448
public DescriptorKey getKey()
@@ -86,6 +90,16 @@ public boolean isAccessAllowed( final PrincipalKeys principalKeys )
8690
return principalKeys.contains( RoleKeys.ADMIN ) || principalKeys.stream().anyMatch( allowedPrincipals::contains );
8791
}
8892

93+
public Set<String> getInterfaces()
94+
{
95+
return interfaces;
96+
}
97+
98+
public boolean hasInterface( final String interfaceName )
99+
{
100+
return interfaces.contains( interfaceName );
101+
}
102+
89103
public boolean isAppLauncherApplication()
90104
{
91105
return displayName != null;
@@ -128,6 +142,8 @@ public static final class Builder
128142

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

145+
private final ImmutableSet.Builder<String> interfaces = ImmutableSet.builder();
146+
131147
private Builder()
132148
{
133149
}
@@ -174,6 +190,12 @@ public Builder apiMounts( final ApiMountDescriptors apiMountDescriptors )
174190
return this;
175191
}
176192

193+
public Builder addInterface( final String interfaceName )
194+
{
195+
this.interfaces.add( interfaceName );
196+
return this;
197+
}
198+
177199
public AdminToolDescriptor build()
178200
{
179201
return new AdminToolDescriptor( this );

modules/admin/admin-api/src/main/java/com/enonic/xp/admin/widget/WidgetDescriptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class WidgetDescriptor
3737

3838
private final ImmutableMap<String, String> config;
3939

40-
private static final String URL_PREFIX = "_/widget/";
40+
private static final String URL_PREFIX = "_/admin/widget/";
4141

4242
private WidgetDescriptor( final Builder builder )
4343
{

modules/admin/admin-impl/src/main/java/com/enonic/xp/admin/impl/portal/WidgetApiHandler.java

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.osgi.service.component.annotations.Component;
99
import org.osgi.service.component.annotations.Reference;
1010

11+
import com.enonic.xp.admin.tool.AdminToolDescriptor;
12+
import com.enonic.xp.admin.tool.AdminToolDescriptorService;
1113
import com.enonic.xp.admin.widget.WidgetDescriptor;
1214
import com.enonic.xp.admin.widget.WidgetDescriptorService;
1315
import com.enonic.xp.app.ApplicationKey;
@@ -22,23 +24,31 @@
2224
import com.enonic.xp.web.WebResponse;
2325
import com.enonic.xp.web.universalapi.UniversalApiHandler;
2426

25-
@Component(immediate = true, service = UniversalApiHandler.class, property = {"applicationKey=widget", "apiKey=",
27+
@Component(immediate = true, service = UniversalApiHandler.class, property = {"applicationKey=admin", "apiKey=widget",
2628
"allowedPrincipals=role:system.admin.login", "allowedPrincipals=role:system.admin"})
2729
public class WidgetApiHandler
2830
implements UniversalApiHandler
2931
{
30-
private static final Pattern WIDGET_API_PATTERN = Pattern.compile( "^/(_|api)/widget/(?<appKey>[^/]+)/(?<widgetKey>[^/]+)" );
32+
private static final Pattern WIDGET_API_PATTERN = Pattern.compile( "^/(_|api)/admin/widget/(?<appKey>[^/]+)/(?<widgetKey>[^/]+)" );
33+
34+
private static final Pattern TOOL_PREFIX_PATTERN = Pattern.compile( "^/admin/(?<appKey>[^/]+)/(?<toolName>[^/]+)" );
35+
36+
private static final String GENERIC_WIDGET_INTERFACE = "generic";
3137

3238
private final ControllerScriptFactory controllerScriptFactory;
3339

3440
private final WidgetDescriptorService widgetDescriptorService;
3541

42+
private final AdminToolDescriptorService adminToolDescriptorService;
43+
3644
@Activate
3745
public WidgetApiHandler( @Reference final ControllerScriptFactory controllerScriptFactory,
38-
@Reference final WidgetDescriptorService widgetDescriptorService )
46+
@Reference final WidgetDescriptorService widgetDescriptorService,
47+
@Reference final AdminToolDescriptorService adminToolDescriptorService )
3948
{
4049
this.controllerScriptFactory = controllerScriptFactory;
4150
this.widgetDescriptorService = widgetDescriptorService;
51+
this.adminToolDescriptorService = adminToolDescriptorService;
4252
}
4353

4454
@Override
@@ -66,6 +76,8 @@ public WebResponse handle( final WebRequest webRequest )
6676
throw WebException.forbidden( String.format( "You don't have permission to access [%s]", descriptorKey ) );
6777
}
6878

79+
verifyMounts( widgetDescriptor, webRequest );
80+
6981
final PortalRequest portalRequest = createPortalRequest( webRequest, descriptorKey );
7082

7183
final ResourceKey script = ResourceKey.from( descriptorKey.getApplicationKey(),
@@ -74,6 +86,29 @@ public WebResponse handle( final WebRequest webRequest )
7486
return controllerScriptFactory.fromScript( script ).execute( portalRequest );
7587
}
7688

89+
private void verifyMounts( final WidgetDescriptor widgetDescriptor, final WebRequest webRequest )
90+
{
91+
if ( !widgetDescriptor.hasInterface( GENERIC_WIDGET_INTERFACE ) && webRequest.getEndpointPath() != null )
92+
{
93+
final Matcher toolMatcher = TOOL_PREFIX_PATTERN.matcher( webRequest.getRawPath() );
94+
if ( toolMatcher.find() )
95+
{
96+
final DescriptorKey toolDescriptorKey =
97+
DescriptorKey.from( resolveApplicationKey( toolMatcher.group( "appKey" ) ), toolMatcher.group( "toolName" ) );
98+
final AdminToolDescriptor adminToolDescriptor = adminToolDescriptorService.getByKey( toolDescriptorKey );
99+
if ( adminToolDescriptor != null && !adminToolDescriptor.getInterfaces().isEmpty() )
100+
{
101+
if ( widgetDescriptor.getInterfaces().stream().noneMatch( adminToolDescriptor::hasInterface ) )
102+
{
103+
throw WebException.notFound(
104+
String.format( "Widget [%s] is not mounted to admin tool [%s]", widgetDescriptor.getKey(),
105+
toolDescriptorKey ) );
106+
}
107+
}
108+
}
109+
}
110+
}
111+
77112
private PortalRequest createPortalRequest( final WebRequest webRequest, final DescriptorKey descriptorKey )
78113
{
79114
final PortalRequest portalRequest =

modules/admin/admin-impl/src/main/java/com/enonic/xp/admin/impl/tool/XmlAdminToolDescriptorParser.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ public class XmlAdminToolDescriptorParser
1919

2020
private static final String API_DESCRIPTOR_TAG_NAME = "api";
2121

22+
private static final String INTERFACES_DESCRIPTOR_TAG_NAME = "interfaces";
23+
24+
private static final String INTERFACE_DESCRIPTOR_TAG_NAME = "interface";
25+
2226
private static final int APPLICATION_KEY_INDEX = 0;
2327

2428
private static final int API_KEY_INDEX = 1;
@@ -55,6 +59,16 @@ protected void doParse( final DomElement root )
5559
}
5660

5761
this.builder.apiMounts( ApiMountDescriptors.from( parseApiMounts( root.getChild( APIS_DESCRIPTOR_TAG_NAME ) ) ) );
62+
63+
final DomElement interfaces = root.getChild( INTERFACES_DESCRIPTOR_TAG_NAME );
64+
if ( interfaces != null )
65+
{
66+
final List<DomElement> interfaceList = interfaces.getChildren( INTERFACE_DESCRIPTOR_TAG_NAME );
67+
for ( DomElement anInterface : interfaceList )
68+
{
69+
this.builder.addInterface( anInterface.getValue().trim() );
70+
}
71+
}
5872
}
5973

6074
private List<ApiMountDescriptor> parseApiMounts( final DomElement apisElement )

modules/admin/admin-impl/src/test/java/com/enonic/xp/admin/impl/portal/WidgetApiHandlerTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.junit.jupiter.api.BeforeEach;
44
import org.junit.jupiter.api.Test;
55

6+
import com.enonic.xp.admin.tool.AdminToolDescriptorService;
67
import com.enonic.xp.admin.widget.WidgetDescriptor;
78
import com.enonic.xp.admin.widget.WidgetDescriptorService;
89
import com.enonic.xp.app.ApplicationKey;
@@ -34,13 +35,16 @@ public class WidgetApiHandlerTest
3435

3536
private WidgetDescriptorService widgetDescriptorService;
3637

38+
private AdminToolDescriptorService adminToolDescriptorService;
39+
3740
@BeforeEach
3841
public void setUp()
3942
{
4043
this.controllerScriptFactory = mock( ControllerScriptFactory.class );
4144
this.widgetDescriptorService = mock( WidgetDescriptorService.class );
45+
this.adminToolDescriptorService = mock( AdminToolDescriptorService.class );
4246

43-
this.handler = new WidgetApiHandler( this.controllerScriptFactory, this.widgetDescriptorService );
47+
this.handler = new WidgetApiHandler( this.controllerScriptFactory, this.widgetDescriptorService, this.adminToolDescriptorService );
4448
}
4549

4650

modules/admin/admin-impl/src/test/java/com/enonic/xp/admin/impl/tool/XmlAdminToolDescriptorParserTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.enonic.xp.admin.impl.tool;
22

3+
import java.util.Set;
4+
35
import org.junit.jupiter.api.BeforeEach;
46
import org.junit.jupiter.api.Test;
57

@@ -15,6 +17,7 @@
1517
import static org.junit.jupiter.api.Assertions.assertEquals;
1618
import static org.junit.jupiter.api.Assertions.assertNotNull;
1719
import static org.junit.jupiter.api.Assertions.assertThrows;
20+
import static org.junit.jupiter.api.Assertions.assertTrue;
1821

1922
public class XmlAdminToolDescriptorParserTest
2023
extends XmlModelParserTest
@@ -73,6 +76,23 @@ public void testParseWithApis()
7376
assertEquals( "", apiMountDescriptor4.getApiKey() );
7477
}
7578

79+
@Test
80+
public void testParseWithInterfaces()
81+
throws Exception
82+
{
83+
parse( this.parser, "-interfaces.xml" );
84+
85+
final AdminToolDescriptor toolDescriptor = this.builder.build();
86+
87+
assertResult( toolDescriptor );
88+
89+
assertEquals( 2, toolDescriptor.getInterfaces().size() );
90+
91+
final Set<String> interfaces = toolDescriptor.getInterfaces();
92+
assertTrue( interfaces.contains( "generic" ) );
93+
assertTrue( interfaces.contains( "admin.dashboard" ) );
94+
}
95+
7696
@Test
7797
public void testParseWithInvalidApis()
7898
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0"?>
2+
<tool xmlns="urn:enonic:xp:model:1.0">
3+
<display-name i18n="key.display-name">My admin tool</display-name>
4+
<description i18n="key.description">Tool description</description>
5+
<allow>
6+
<principal>role:system.admin</principal>
7+
</allow>
8+
<interfaces>
9+
<interface>generic</interface>
10+
<interface>admin.dashboard</interface>
11+
</interfaces>
12+
</tool>

modules/core/core-api/src/main/resources/META-INF/xsd/model.xsd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,13 @@
301301
</xs:complexType>
302302
</xs:element>
303303
<xs:element name="apis" minOccurs="0" type="apiMount"/>
304+
<xs:element minOccurs="0" name="interfaces">
305+
<xs:complexType>
306+
<xs:sequence>
307+
<xs:element maxOccurs="unbounded" minOccurs="0" name="interface" type="xs:string"/>
308+
</xs:sequence>
309+
</xs:complexType>
310+
</xs:element>
304311
</xs:sequence>
305312
</xs:complexType>
306313

0 commit comments

Comments
 (0)