Skip to content

Commit 595b5d7

Browse files
committed
Bug #13477
Fix the bug: now the publications aliases cannot be shared to other people for security reasons. Indeed, the publication aliasing covers two features: * A way to allow access for publications to users having no access for the EDM in which those publications are located. * A way to multi categorize a publication; For the first point, for security reasons, the users have only read rights on the publication aliases. They have then all the functions mapped to their role that don't break this read-only constraint. Only users having access for the EDM or the folder in the EDM in which the alias has been created can then access, through the alias, for the publication. For the second point, the multi categorization feature is limited by the way the publications cannot be directly modified in those different categorizations; they cannot be modified by their aliases. In fact, the feature is more a consequence of the first one than a thinkfully one. Beside this, the folder or publication sharing is a way to share information both to other users in Silverpeas and external users. This is why the feature can be a way to counterpass the access constraint on publications with aliases. So, this fix ensures now the publication aliases cannot be shared to other people than those expected by the contributor who created the aliases.
1 parent 428aa0e commit 595b5d7

File tree

24 files changed

+150
-137
lines changed

24 files changed

+150
-137
lines changed

core-library/src/main/java/org/silverpeas/core/contribution/publication/model/Location.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public int hashCode() {
118118
return super.hashCode();
119119
}
120120

121-
public class Alias implements Serializable {
121+
public static class Alias implements Serializable {
122122
private String userId;
123123
private transient String userName;
124124
private Date date;

core-library/src/main/java/org/silverpeas/core/contribution/publication/model/PublicationDetail.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,8 +1067,8 @@ public String getPermalink() {
10671067
}
10681068

10691069
public boolean isSharingAllowedForRolesFrom(final UserDetail user) {
1070-
if (!isValid()) {
1071-
// a not valid publication can not be shared
1070+
if (!isValid() || isAlias()) {
1071+
// neither a non validated publication nor an alias can not be shared
10721072
return false;
10731073
}
10741074

core-library/src/main/java/org/silverpeas/core/contribution/publication/service/DefaultPublicationService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,17 @@ public Collection<PublicationDetail> getDetailsByFatherPK(NodePK fatherPK, Strin
752752
}
753753
}
754754

755+
@Override
756+
public Collection<PublicationDetail> getVisiblePublicationsIn(NodePK fatherPK) {
757+
Collection<PublicationDetail> publications = getDetailsByFatherPK(fatherPK, null, true);
758+
for (PublicationDetail publication : publications) {
759+
var aliases = getAllAliases(publication.getPK());
760+
//noinspection SuspiciousMethodCalls
761+
publication.setAlias(aliases.contains(fatherPK));
762+
}
763+
return publications;
764+
}
765+
755766
@Override
756767
public Collection<PublicationDetail> getDetailsByFatherPK(NodePK fatherPK, String sorting,
757768
boolean filterOnVisibilityPeriod, String userId) {

core-library/src/main/java/org/silverpeas/core/contribution/publication/service/PublicationService.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,15 +278,16 @@ Pair<Collection<Location>, Collection<Location>> setAliases(PublicationPK pubPK,
278278
void removeAliases(PublicationPK pubPK, Collection<Location> aliases);
279279

280280
/**
281-
* Gets all the publications attached to the specified father.
281+
* Gets all the publications attached to the specified father. The aliasing property of the
282+
* publications isn't set.
282283
* @param fatherPK the identifying key of the father.
283284
* @return a collection of {@link PublicationDetail} instances.
284285
*/
285286
Collection<PublicationDetail> getDetailsByFatherPK(NodePK fatherPK);
286287

287288
/**
288289
* Gets all the publications attached to the specified father ordered as indicated by the sorting
289-
* directive.
290+
* directive. The aliasing property of the publications isn't set.
290291
* @param fatherPK the identifying key of the father.
291292
* @param sorting a sorting directive. Must be in the form of
292293
* "P.[publication detail attribute] (DESC|ASC)"
@@ -296,7 +297,7 @@ Pair<Collection<Location>, Collection<Location>> setAliases(PublicationPK pubPK,
296297

297298
/**
298299
* Gets all the publications attached to the specified father, ordered as indicated by the sorting
299-
* directive, according to the visibility.
300+
* directive, according to the visibility. The aliasing property of the publications isn't set.
300301
* @param fatherPK the identifying key of the father.
301302
* @param sorting a sorting directive. Must be in the form of
302303
* "P.[publication detail attribute] (DESC|ASC)"
@@ -305,6 +306,14 @@ Pair<Collection<Location>, Collection<Location>> setAliases(PublicationPK pubPK,
305306
*/
306307
Collection<PublicationDetail> getDetailsByFatherPK(NodePK fatherPK, String sorting, boolean filterOnVisibilityPeriod);
307308

309+
/**
310+
* Gets all the publications that are currently visible and located into the specified node.
311+
* For each returned publications, their aliasing property is valued.
312+
* @param fatherPK the unique identifier of the node father of the publications.
313+
* @return a list of {@link PublicationDetail} instances.
314+
*/
315+
Collection<PublicationDetail> getVisiblePublicationsIn(NodePK fatherPK);
316+
308317
/**
309318
* Gets all the publications attached to the specified father, ordered as indicated by the sorting
310319
* directive, according to the visibility, and that was authored or updated by the specified

core-library/src/main/java/org/silverpeas/core/util/OsEnum.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public enum OsEnum {
3131
WINDOWS_XP("Windows XP", true), WINDOWS_9X("win9x", true), WINDOWS_VISTA("Windows Vista", true),
3232
WINDOWS_SEVEN("Windows 7", true), LINUX("Linux", false), MAC_OSX("Mac OS X", false),
3333
OS_400("os/400", false), Z_OS("z/os", false), OPENVMS("openvms", false), NETWARE("netware", false);
34-
protected final boolean windows;
35-
protected final String name;
34+
private final boolean windows;
35+
private final String name;
3636

3737
OsEnum(String name, boolean windows) {
3838
this.windows = windows;

core-library/src/main/java/org/silverpeas/core/util/file/FileUtil.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
import java.io.IOException;
4747
import java.nio.charset.Charset;
4848
import java.nio.file.Files;
49+
import java.nio.file.InvalidPathException;
4950
import java.nio.file.Path;
51+
import java.nio.file.Paths;
5052
import java.util.ArrayList;
5153
import java.util.Comparator;
5254
import java.util.List;
@@ -104,7 +106,7 @@ public static String getMimeType(final String fileName) {
104106
private static String computeMimeType(final String fileName) {
105107
String mimeType = null;
106108
File file = new File(fileName);
107-
if (file.exists()) {
109+
if (Files.exists(file.toPath())) {
108110
mimeType = getMimeTypeByMetadata(file);
109111
}
110112
final String fileExtension = FileRepositoryManager.getFileExtension(fileName).toLowerCase();
@@ -448,10 +450,33 @@ public static void validateFilename(String fileName, String intendedDir)
448450
* @param value a string data representing a path.
449451
* @return the given data.
450452
*/
451-
public static String verifyTaintedData(String value) {
453+
public static String checkTaintedData(String value) {
452454
if (isDefined(value) && value.contains("..")) {
453455
throw new IllegalArgumentException(String.format("Value '%s' is forbidden", value));
454456
}
455457
return value;
456458
}
459+
460+
/**
461+
* Checks the specified file name doesn't contain invalid characters for the underlying OS,
462+
* otherwise it throws an {@link java.nio.file.InvalidPathException} exception. If an attempt
463+
* to go back in the filesystem is detected, then an {@link IllegalArgumentException} is thrown.
464+
* @param fileName the name of the file
465+
* @throws IllegalArgumentException if the filename is empty, exceeds the number of characters
466+
* limitation or is a path.
467+
* @throws InvalidPathException if the filename contains invalid characters for the underlying OS.
468+
*/
469+
public static void checkFileName(String fileName) {
470+
if (fileName == null || fileName.isEmpty() || fileName.length() > 255) {
471+
throw new InvalidPathException(fileName == null ? "": fileName,
472+
"The file name is invalid: either it is empty or exceeds the size limit");
473+
}
474+
boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
475+
if ((isWindows && fileName.contains("\\")) || fileName.contains("/")) {
476+
throw new IllegalArgumentException(String.format(
477+
"File name '%s' isn't a filename but a path", fileName));
478+
}
479+
checkTaintedData(fileName);
480+
Paths.get(fileName);
481+
}
457482
}

core-library/src/test/java/org/silverpeas/core/util/FileUtilTest.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -268,23 +268,23 @@ void testValidateFileNameKo() {
268268
}
269269

270270
@Test
271-
void testVerifyTaintedData() {
272-
assertThat(FileUtil.verifyTaintedData("a.b"), is("a.b"));
273-
assertThat(FileUtil.verifyTaintedData(""), is(""));
274-
assertThat(FileUtil.verifyTaintedData("null"), is("null"));
275-
assertThat(FileUtil.verifyTaintedData("."), is("."));
276-
assertThat(FileUtil.verifyTaintedData(File.separator + "."), is(File.separator + "."));
277-
assertThat(FileUtil.verifyTaintedData("." + File.separator), is("." + File.separator));
271+
void testCheckTaintedData() {
272+
assertThat(FileUtil.checkTaintedData("a.b"), is("a.b"));
273+
assertThat(FileUtil.checkTaintedData(""), is(""));
274+
assertThat(FileUtil.checkTaintedData("null"), is("null"));
275+
assertThat(FileUtil.checkTaintedData("."), is("."));
276+
assertThat(FileUtil.checkTaintedData(File.separator + "."), is(File.separator + "."));
277+
assertThat(FileUtil.checkTaintedData("." + File.separator), is("." + File.separator));
278278
assertThrows(IllegalArgumentException.class,
279-
() -> FileUtil.verifyTaintedData(File.separator + ".." + File.separator));
279+
() -> FileUtil.checkTaintedData(File.separator + ".." + File.separator));
280280
assertThrows(IllegalArgumentException.class,
281-
() -> FileUtil.verifyTaintedData(".." + File.separator));
281+
() -> FileUtil.checkTaintedData(".." + File.separator));
282282
assertThrows(IllegalArgumentException.class,
283-
() -> FileUtil.verifyTaintedData(File.separator + ".."));
283+
() -> FileUtil.checkTaintedData(File.separator + ".."));
284284
assertThrows(IllegalArgumentException.class,
285-
() -> FileUtil.verifyTaintedData(".."));
285+
() -> FileUtil.checkTaintedData(".."));
286286
assertThrows(IllegalArgumentException.class,
287-
() -> FileUtil.verifyTaintedData("a..b"));
287+
() -> FileUtil.checkTaintedData("a..b"));
288288
}
289289

290290
}

core-services/sharing/src/main/java/org/silverpeas/core/sharing/model/NodeAccessControl.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
*/
4242
public class NodeAccessControl extends AbstractShareableAccessControl {
4343

44-
NodeAccessControl(NodeTicket ticket) {
44+
NodeAccessControl(Ticket ticket) {
4545
super(ticket);
4646
}
4747

@@ -64,7 +64,7 @@ protected Collection<NodePK> getPublicationFathers(ResourceReference pk) {
6464
return getPublicationService().getAllFatherPKInSamePublicationComponentInstance(new PublicationPK(pk.getId(), pk.getInstanceId()));
6565
}
6666

67-
protected Collection<Location> getPublicationAliases(ResourceReference pk) {
67+
protected Collection<Location> getPublicationLocations(ResourceReference pk) {
6868
return getPublicationService().getAllLocations(new PublicationPK(pk.getId(),
6969
pk.getInstanceId()));
7070
}
@@ -81,10 +81,10 @@ private boolean isPublicationReadable(ResourceReference pk, String instanceId,
8181
} else {
8282
// special case of an alias between two ECM applications
8383
// check if publication which contains attachment is an alias into this node
84-
Collection<Location> locations = getPublicationAliases(pk);
84+
Collection<Location> locations = getPublicationLocations(pk);
8585
for (Location location : locations) {
86-
NodePK aliasPK = new NodePK(location.getId(), location.getInstanceId());
87-
if (authorizedNodes.contains(aliasPK)) {
86+
NodePK father = new NodePK(location.getId(), location.getInstanceId());
87+
if (!location.isAlias() && authorizedNodes.contains(father)) {
8888
return true;
8989
}
9090
}

core-services/sharing/src/main/java/org/silverpeas/core/sharing/model/NodeTicket.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.silverpeas.core.node.model.NodeDetail;
2727
import org.silverpeas.core.node.model.NodePK;
2828
import org.silverpeas.core.node.service.NodeService;
29-
import org.silverpeas.core.sharing.security.AccessControlContext;
3029
import org.silverpeas.core.sharing.security.ShareableAccessControl;
3130
import org.silverpeas.core.sharing.security.ShareableNode;
3231
import org.silverpeas.core.sharing.security.ShareableResource;

core-services/sharing/src/main/java/org/silverpeas/core/sharing/model/PublicationAccessControl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
*/
3434
public class PublicationAccessControl extends AbstractShareableAccessControl {
3535

36-
PublicationAccessControl(PublicationTicket ticket) {
36+
PublicationAccessControl(Ticket ticket) {
3737
super(ticket);
3838
}
3939

core-services/sharing/src/main/java/org/silverpeas/core/sharing/model/SimpleFileAccessControl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
*/
3232
public class SimpleFileAccessControl extends AbstractShareableAccessControl {
3333

34-
SimpleFileAccessControl(SimpleFileTicket ticket) {
34+
SimpleFileAccessControl(Ticket ticket) {
3535
super(ticket);
3636
}
3737

core-services/sharing/src/main/java/org/silverpeas/core/sharing/model/VersionFileAccessControl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
*/
3232
public class VersionFileAccessControl extends AbstractShareableAccessControl {
3333

34-
VersionFileAccessControl(VersionFileTicket ticket) {
34+
VersionFileAccessControl(Ticket ticket) {
3535
super(ticket);
3636
}
3737

core-services/sharing/src/main/java/org/silverpeas/core/sharing/security/AbstractShareableAccessControl.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ protected AbstractShareableAccessControl(Ticket ticket) {
3737
this.ticket = ticket;
3838
}
3939

40-
@Override
41-
public abstract boolean isReadable(final AccessControlContext resource);
42-
4340
protected Ticket getSharingTicket() {
4441
return ticket;
4542
}

core-war/src/main/java/org/silverpeas/web/sharing/servlets/FileSharingRequestRouter.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,6 @@ public class FileSharingRequestRouter extends ComponentRequestRouter<FileSharing
4949

5050
private static final long serialVersionUID = -8855028133035807994L;
5151

52-
/**
53-
* This method has to be implemented in the component request rooter class. returns the session
54-
* control bean name to be put in the request object ex : for almanach, returns "almanach"
55-
*/
5652
@Override
5753
public String getSessionControlBeanName() {
5854
return "FileSharing";

core-web/src/main/java/org/silverpeas/core/webapi/attachment/AbstractAttachmentResource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public String getComponentId() {
5151
return componentId;
5252
}
5353

54-
protected Response getFileContent(String attachmentId) {
54+
protected Response getAttachmentContent(String attachmentId) {
5555
final SimpleDocument attachment = AttachmentServiceProvider.getAttachmentService()
5656
.searchDocumentById(new SimpleDocumentPK(attachmentId), null);
5757
if (attachment == null) {

core-web/src/main/java/org/silverpeas/core/webapi/attachment/AbstractSimpleDocumentResource.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@ protected String getBundleLocation() {
5959
return "org.silverpeas.util.attachment.multilang.attachment";
6060
}
6161

62-
/**
63-
* Check the file.
64-
* @param fileToCheck
65-
*/
6662
protected void checkUploadedFile(File fileToCheck) {
6763
try {
6864

@@ -85,10 +81,6 @@ protected void checkUploadedFile(File fileToCheck) {
8581
}
8682
}
8783

88-
/**
89-
* Manages the runtime errors.
90-
* @param re
91-
*/
9284
protected void performRuntimeException(RuntimeException re) {
9385
String transverseExceptionMessage = SilverpeasTransverseErrorUtil
9486
.performExceptionMessage(re, getUserPreferences().getLanguage());

core-web/src/main/java/org/silverpeas/core/webapi/attachment/AttachmentResource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ protected String getResourceBasePath() {
5454
@Path("{id}/{name}")
5555
@Produces(MediaType.APPLICATION_OCTET_STREAM)
5656
public Response getFileContent(final @PathParam("id") String attachmentId) {
57-
return super.getFileContent(attachmentId);
57+
return getAttachmentContent(attachmentId);
5858
}
5959

6060
@Override

core-web/src/main/java/org/silverpeas/core/webapi/attachment/SharedAttachmentResource.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.silverpeas.core.util.MimeTypes;
3636
import org.silverpeas.core.util.ZipUtil;
3737
import org.silverpeas.core.util.file.FileRepositoryManager;
38+
import org.silverpeas.core.util.file.FileUtil;
3839

3940
import javax.ws.rs.*;
4041
import javax.ws.rs.core.MediaType;
@@ -43,6 +44,7 @@
4344
import javax.ws.rs.core.UriBuilderException;
4445
import java.io.*;
4546
import java.net.URI;
47+
import java.nio.file.Files;
4648
import java.util.StringTokenizer;
4749
import java.util.UUID;
4850

@@ -67,7 +69,7 @@ protected String getResourceBasePath() {
6769
@Path("{id}/{name}")
6870
@Produces(MediaType.APPLICATION_OCTET_STREAM)
6971
public Response getFileContent(final @PathParam("id") String attachmentId) {
70-
return super.getFileContent(attachmentId);
72+
return getAttachmentContent(attachmentId);
7173
}
7274

7375
@GET
@@ -112,9 +114,10 @@ public ZipEntity zipFiles(@PathParam("ids") String attachmentIds) {
112114
@Path("zipcontent/{name}")
113115
@Produces(MediaType.APPLICATION_OCTET_STREAM)
114116
public Response getZipContent(@PathParam("name") String zipFileName) {
117+
checkFileName(zipFileName);
115118
String zipFilePath = FileRepositoryManager.getTemporaryPath() + zipFileName;
116119
File realFile = new File(zipFilePath);
117-
if (!realFile.exists() && !realFile.isFile()) {
120+
if (!Files.exists(realFile.toPath()) && !Files.isRegularFile(realFile.toPath())) {
118121
return Response.ok().build();
119122
} else {
120123
try(ByteArrayOutputStream output = new ByteArrayOutputStream();
@@ -140,4 +143,12 @@ protected String getToken() {
140143
return token;
141144
}
142145

146+
private void checkFileName(String fileName) {
147+
try {
148+
FileUtil.checkFileName(fileName);
149+
} catch (IllegalArgumentException e) {
150+
throw new BadRequestException(e.getMessage());
151+
}
152+
}
153+
143154
}

0 commit comments

Comments
 (0)