Skip to content

Commit

Permalink
Media handler #10869
Browse files Browse the repository at this point in the history
  • Loading branch information
anatol-sialitski committed Jan 22, 2025
1 parent 2b56c4e commit fd2dab8
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public class MediaHandler

private static final EnumSet<HttpMethod> ALLOWED_METHODS = EnumSet.of( HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS );

private static final Predicate<WebRequest> IS_GET_HEAD_OPTIONS_METHOD = req -> ALLOWED_METHODS.contains( req.getMethod() );
private static final Predicate<WebRequest> IS_ALLOWED_METHOD = req -> ALLOWED_METHODS.contains( req.getMethod() );

private static final MediaType SVG_MEDIA_TYPE = MediaType.SVG_UTF_8.withoutParameters();

Expand Down Expand Up @@ -132,10 +132,10 @@ public WebResponse handle( final WebRequest webRequest, final WebResponse webRes
final Matcher matcher = PATTERN.matcher( Objects.requireNonNullElse( webRequest.getEndpointPath(), webRequest.getRawPath() ) );
if ( !matcher.matches() )
{
return PortalResponse.create( webResponse ).status( HttpStatus.NOT_FOUND ).build();
throw createNotFoundException();
}

if ( !IS_GET_HEAD_OPTIONS_METHOD.test( webRequest ) )
if ( !IS_ALLOWED_METHOD.test( webRequest ) )
{
throw new WebException( HttpStatus.METHOD_NOT_ALLOWED, String.format( "Method %s not allowed", webRequest.getMethod() ) );
}
Expand All @@ -145,6 +145,11 @@ public WebResponse handle( final WebRequest webRequest, final WebResponse webRes
return HandlerHelper.handleDefaultOptions( ALLOWED_METHODS );
}

if ( !defaultContextPathVerifier.verify( webRequest ) )
{
throw createNotFoundException();
}

final RepositoryId repositoryId =
HandlerHelper.resolveRepositoryId( ProjectConstants.PROJECT_REPO_ID_PREFIX + matcher.group( "project" ) );
final Branch branch = HandlerHelper.resolveBranch( Objects.requireNonNullElse( matcher.group( "branch" ), "master" ) );
Expand All @@ -153,11 +158,6 @@ public WebResponse handle( final WebRequest webRequest, final WebResponse webRes
final String fingerprint = matcher.group( "fingerprint" );
final String restPath = matcher.group( "restPath" );

if ( !defaultContextPathVerifier.verify( webRequest ) )
{
throw WebException.notFound( "Not a valid media url pattern" );
}

verifyMediaScope( matcher.group( "context" ), repositoryId, branch, webRequest );

final PortalRequest portalRequest = createPortalRequest( webRequest, repositoryId, branch );
Expand All @@ -175,19 +175,26 @@ private void verifyMediaScope( final String projectContext, final RepositoryId r
{
if ( MEDIA_SCOPE_DELIMITER_PATTERN.splitAsStream( mediaServiceScope ).map( String::trim ).noneMatch( projectContext::equals ) )
{
throw WebException.notFound( "Not a valid media url pattern" );
throw createNotFoundException();
}
}
else

if ( webRequest.getRawPath().startsWith( "/api/" ) )
{
if ( webRequest instanceof final PortalRequest portalRequest )
{
if ( portalRequest.isSiteBase() &&
!( repositoryId.equals( portalRequest.getRepositoryId() ) && branch.equals( portalRequest.getBranch() ) ) )
{
throw WebException.notFound( "Not a valid media url pattern" );
}
}
return;
}

if ( !( webRequest.getRawPath().startsWith( "/site/" ) || webRequest.getRawPath().startsWith( "/admin/site/" ) ) )
{
throw createNotFoundException();
}

final PortalRequest portalRequest =
webRequest instanceof PortalRequest ? (PortalRequest) webRequest : new PortalRequest( webRequest );

if ( !( repositoryId.equals( portalRequest.getRepositoryId() ) && branch.equals( portalRequest.getBranch() ) ) )
{
throw createNotFoundException();
}
}

Expand Down Expand Up @@ -224,7 +231,7 @@ private PortalResponse doHandleImage( final PortalRequest portalRequest, final C
final Matcher matcher = IMAGE_REST_PATH_PATTERN.matcher( restPath );
if ( !matcher.matches() )
{
throw WebException.notFound( "Not a valid image url pattern" );
throw createNotFoundException();

Check warning on line 234 in modules/portal/portal-impl/src/main/java/com/enonic/xp/portal/impl/handler/MediaHandler.java

View check run for this annotation

Codecov / codecov/patch

modules/portal/portal-impl/src/main/java/com/enonic/xp/portal/impl/handler/MediaHandler.java#L234

Added line #L234 was not covered by tests
}

final Content content = getContent( id );
Expand Down Expand Up @@ -470,4 +477,9 @@ private ByteSource transform( final Media content, final BinaryReference binaryR
throw new WebException( HttpStatus.TOO_MANY_REQUESTS, "Try again later", e );
}
}

private WebException createNotFoundException()
{
return WebException.notFound( "Not a valid media url pattern" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,16 @@ private void setupMedia()

@Test
void testInvalidUrl()
throws Exception
{
this.request.setEndpointPath( null );
this.request.setRawPath( "/api/attachment/myproject/123456:ec25d6e4126c7064f82aaab8b34693fc/logo.png" );

final PortalResponse res = (PortalResponse) this.handler.handle( this.request, PortalResponse.create().build() );
assertNotNull( res );
assertEquals( HttpStatus.NOT_FOUND, res.getStatus() );
WebException ex = assertThrows( WebException.class, () -> this.handler.handle( this.request, WebResponse.create().build() ) );
assertEquals( HttpStatus.NOT_FOUND, ex.getStatus() );
}

@Test
public void testAttachment()
void testAttachment()
throws Exception
{
setupMedia();
Expand All @@ -138,41 +136,31 @@ public void testAttachment()
}

@Test
public void testAttachmentForEndpointOnAdmin()
throws Exception
void testAttachmentForEndpointOnAdmin()
{
setupMedia();

this.request.setEndpointPath( "/_/media/attachment/myproject/123456:ec25d6e4126c7064f82aaab8b34693fc/logo.png" );
this.request.setRawPath( "/admin/_/media/attachment/myproject/123456:ec25d6e4126c7064f82aaab8b34693fc/logo.png" );
this.request.setRawPath( "/admin/app/toolName/_/media/attachment/myproject/123456:ec25d6e4126c7064f82aaab8b34693fc/logo.png" );

final PortalResponse res = (PortalResponse) this.handler.handle( this.request, PortalResponse.create().build() );
assertNotNull( res );
assertEquals( HttpStatus.OK, res.getStatus() );
assertEquals( MediaType.PNG, res.getContentType() );
assertNull( res.getHeaders().get( "Content-Disposition" ) );
assertSame( this.mediaBytes, res.getBody() );
WebException ex = assertThrows( WebException.class, () -> this.handler.handle( this.request, WebResponse.create().build() ) );
assertEquals( HttpStatus.NOT_FOUND, ex.getStatus() );
}

@Test
public void testAttachmentForEndpointOnWebApp()
throws Exception
void testAttachmentForEndpointOnWebApp()
{
setupMedia();

this.request.setEndpointPath( "/_/media/attachment/myproject/123456/logo.png" );
this.request.setRawPath( "/webapp/com.enonic.app.mywebapp/_/media/attachment/myproject/123456/logo.png" );

final PortalResponse res = (PortalResponse) this.handler.handle( this.request, PortalResponse.create().build() );
assertNotNull( res );
assertEquals( HttpStatus.OK, res.getStatus() );
assertEquals( MediaType.PNG, res.getContentType() );
assertNull( res.getHeaders().get( "Content-Disposition" ) );
assertSame( this.mediaBytes, res.getBody() );
WebException ex = assertThrows( WebException.class, () -> this.handler.handle( this.request, WebResponse.create().build() ) );
assertEquals( HttpStatus.NOT_FOUND, ex.getStatus() );
}

@Test
public void testInvalidContextPath()
void testInvalidContextPath()
{
setupMedia();

Expand All @@ -185,7 +173,7 @@ public void testInvalidContextPath()
}

@Test
public void testAttachmentForEndpointOnSite()
void testAttachmentForEndpointOnSite()
throws Exception
{
setupMedia();
Expand Down Expand Up @@ -213,7 +201,7 @@ public void testAttachmentForEndpointOnSite()
}

@Test
public void testMediaEndpointDoesNotAllowedIfSiteContextDoesNotMatch()
void testMediaEndpointDoesNotAllowedIfSiteContextDoesNotMatch()
{
setupMedia();

Expand All @@ -237,7 +225,7 @@ public void testMediaEndpointDoesNotAllowedIfSiteContextDoesNotMatch()
}

@Test
public void testAttachmentDownload()
void testAttachmentDownload()
throws Exception
{
setupMedia();
Expand All @@ -263,7 +251,7 @@ public void testAttachmentDownload()
}

@Test
public void testAttachmentDraftBranchForNotAuthorizedUser()
void testAttachmentDraftBranchForNotAuthorizedUser()
{
this.request.setRawPath( "/api/media/attachment/myproject:draft/123456/logo.png" );
this.request.setBranch( ContentConstants.BRANCH_DRAFT );
Expand All @@ -275,11 +263,12 @@ public void testAttachmentDraftBranchForNotAuthorizedUser()
}

@Test
public void testImage()
void testImage()
throws Exception
{
setupContent();

this.request.setEndpointPath( null );
this.request.setRawPath( "/api/media/image/myproject/123456/scale-100-100/image-name.jpg" );

WebResponse res = this.handler.handle( this.request, PortalResponse.create().build() );
Expand All @@ -298,9 +287,10 @@ public void testImage()
}

@Test
public void testOptions()
void testOptions()
throws Exception
{
this.request.setEndpointPath( null );
this.request.setRawPath( "/api/media/attachment/myproject:draft/123456/logo.png" );
this.request.setMethod( HttpMethod.OPTIONS );

Expand All @@ -314,6 +304,7 @@ public void testOptions()
void testHandleMethodNotAllowed()
{
this.request.setMethod( HttpMethod.DELETE );
this.request.setEndpointPath( null );
this.request.setRawPath( "/api/media/attachment/myproject:draft/123456/logo.png" );

WebException ex = assertThrows( WebException.class, () -> this.handler.handle( this.request, WebResponse.create().build() ) );
Expand Down Expand Up @@ -373,18 +364,21 @@ void testMediaScope()
webResponse = this.handler.handle( this.request, PortalResponse.create().build() );
assertEquals( HttpStatus.OK, webResponse.getStatus() );

this.request.setEndpointPath( "/_/media/image/myproject/123456/scale-100-100/image-name.jpg" );
this.request.setRawPath( "/admin/_/media/image/myproject/123456/scale-100-100/image-name.jpg" );
this.request.setEndpointPath( "/_/media/image/myproject:draft/123456/scale-100-100/image-name.jpg" );
this.request.setRawPath(
"/admin/site/preview/myproject/draft/_/media/image/myproject:draft/123456/scale-100-100/image-name.jpg" );
webResponse = this.handler.handle( this.request, PortalResponse.create().build() );
assertEquals( HttpStatus.OK, webResponse.getStatus() );

this.request.setEndpointPath( "/_/media/image/myproject:draft/123456/scale-100-100/image-name.jpg" );
this.request.setRawPath( "/admin/_/media/image/myproject:draft/123456/scale-100-100/image-name.jpg" );
this.request.setRawPath(
"/admin/site/preview/myproject/master/_/media/image/myproject:draft/123456/scale-100-100/image-name.jpg" );
webResponse = this.handler.handle( this.request, PortalResponse.create().build() );
assertEquals( HttpStatus.OK, webResponse.getStatus() );

this.request.setEndpointPath( "/_/media/image/unknown:draft/123456/scale-100-100/image-name.jpg" );
this.request.setRawPath( "/admin/_/media/image/unknown:draft/123456/scale-100-100/image-name.jpg" );
this.request.setRawPath(
"/admin/site/preview/unknown/draft/_/media/image/unknown:draft/123456/scale-100-100/image-name.jpg" );
WebException ex =
assertThrows( WebException.class, () -> this.handler.handle( this.request, PortalResponse.create().build() ) );
assertEquals( HttpStatus.NOT_FOUND, ex.getStatus() );
Expand All @@ -404,7 +398,7 @@ void svgzImage()
when( mediaInfoService.getImageOrientation( any( ByteSource.class ) ) ).thenReturn( ImageOrientation.LeftBottom );

this.request.setEndpointPath( "/_/media/image/myproject/123456/full/image-name.svgz" );
this.request.setRawPath( "/admin/_/media/image/myproject/123456/full/image-name.svgz" );
this.request.setRawPath( "/admin/site/preview/myproject/master/_/media/image/myproject/123456/full/image-name.svgz" );

final WebResponse res = this.handler.handle( this.request, PortalResponse.create().build() );
assertNotNull( res );
Expand All @@ -417,13 +411,13 @@ void svgzImage()
}

@Test
void gifImage()
void testGifImage()
throws Exception
{
setupContentGif();

this.request.setEndpointPath( "/_/media/image/myproject/123456/full/image-name.gif" );
this.request.setRawPath( "/admin/_/media/image/myproject/123456/full/image-name.svgz" );
this.request.setRawPath( "/site/myproject/master/sitepath/_/media/image/myproject/123456/full/image-name.gif" );

final WebResponse res = this.handler.handle( this.request, PortalResponse.create().build() );
assertNotNull( res );
Expand Down

0 comments on commit fd2dab8

Please sign in to comment.