Skip to content

Commit ce33ba0

Browse files
committed
Optimizing the JCR Access Control routines.
1 parent 92f7729 commit ce33ba0

File tree

6 files changed

+63
-158
lines changed

6 files changed

+63
-158
lines changed

src/main/java/org/silverpeas/jcr/auth/AbstractAuthentication.java

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,13 @@
2525

2626
import org.silverpeas.jcr.jaas.SilverpeasJcrSystemPrincipal;
2727
import org.silverpeas.jcr.jaas.SilverpeasUserPrincipal;
28-
import org.silverpeas.jcr.jaas.SilverpeasUserProfile;
2928

3029
import javax.naming.InitialContext;
3130
import javax.naming.NamingException;
3231
import javax.sql.DataSource;
3332
import java.security.Principal;
3433
import java.sql.Connection;
35-
import java.sql.PreparedStatement;
36-
import java.sql.ResultSet;
3734
import java.sql.SQLException;
38-
import java.util.logging.Level;
39-
import java.util.logging.Logger;
4035

4136
/**
4237
* This class defines common operations authentication mechanisms can require in order to perform
@@ -45,34 +40,15 @@
4540
*/
4641
public abstract class AbstractAuthentication implements Authentication {
4742

48-
private static final String SELECT_USER_ROLES =
49-
"select r.rolename, r.instanceid, c.componentname from st_userrole_user_rel u join " +
50-
"st_userrole r on u.userroleid = r.id join st_componentinstance c on " +
51-
"c.id = r.instanceid where u.userid = ?";
52-
53-
protected Principal getSilverpeasUserPrincipal(final SilverpeasUser user) {
54-
Principal principal;
43+
protected Principal getSilverpeasUserPrincipal(final SilverpeasUser user,
44+
final String authorizedDocumentPath) {
45+
final Principal principal;
5546
if (user.isJcrSystemUser()) {
5647
principal = new SilverpeasJcrSystemPrincipal();
5748
} else {
58-
try (Connection connection = openConnectionToDataSource();
59-
PreparedStatement statement = connection.prepareStatement(SELECT_USER_ROLES)) {
60-
statement.setInt(1, Integer.parseInt(user.getId()));
61-
try (ResultSet resultSet = statement.executeQuery()) {
62-
principal = new SilverpeasUserPrincipal(user.getId(), "A".equals(user.getAccessLevel()));
63-
while (resultSet.next()) {
64-
String componentInstanceId = resultSet.getString("instanceid");
65-
String componentName = resultSet.getString("componentname");
66-
String roleName = resultSet.getString("rolename");
67-
SilverpeasUserProfile profile =
68-
new SilverpeasUserProfile(componentName + componentInstanceId, roleName);
69-
((SilverpeasUserPrincipal) principal).addUserProfile(profile);
70-
}
71-
}
72-
} catch (SQLException e) {
73-
principal = null;
74-
Logger.getLogger(getClass().getSimpleName()).log(Level.SEVERE, e.getMessage(), e);
75-
}
49+
principal = new SilverpeasUserPrincipal(user.getId(),
50+
"A".equals(user.getAccessLevel()),
51+
authorizedDocumentPath);
7652
}
7753
return principal;
7854
}

src/main/java/org/silverpeas/jcr/auth/SQLSimpleAuthentication.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ public class SQLSimpleAuthentication extends AbstractAuthentication {
5353
private static final String SELECT_DOMAIN_TABLE =
5454
"select propfilename, classname from st_domain where id = ?";
5555

56-
private static final String SELECT_USER_DATA =
57-
"select du.id as id, du.password as password, u.accesslevel as accesslevel from " +
58-
"{0}_user du left join st_user u on du.id = u.id where du.login = ? and u.state = ''VALID''";
56+
private static final String SELECT_DOMAIN_USER_DATA =
57+
"SELECT du.id as id, du.password as password, u.accesslevel as accesslevel FROM " +
58+
"{0}_user du LEFT JOIN st_user u ON CAST(du.id AS VARCHAR(100)) = u.specificid " +
59+
"WHERE u.domainid = ? AND du.login = ? AND u.state = ''VALID''";
5960

6061
/**
6162
* Authenticates a user by its credentials.
@@ -107,7 +108,7 @@ public Principal authenticate(SimpleCredentials credentials) throws Authenticati
107108
throw new AuthenticationException(error.getMessage());
108109
}
109110
}
110-
principal = getSilverpeasUserPrincipal(user);
111+
principal = getSilverpeasUserPrincipal(user, null);
111112
} else {
112113
throw new AuthenticationException("No user matching the login " + login +
113114
" and the domain identifier " + domainId);
@@ -138,9 +139,11 @@ private SilverpeasUser getSilverpeasUserByDomain(final String login, final Strin
138139
if (className != null && (className.toLowerCase().endsWith("sqldriver") ||
139140
className.toLowerCase().endsWith("silverpeasdomaindriver"))) {
140141
String domainName = fetchDomainNameFrom(domainResultSet.getString("propfilename"));
141-
String sqlUserData = computeUserDataSQLQueryFor(domainName);
142-
try (PreparedStatement userStatement = connection.prepareStatement(sqlUserData)) {
143-
userStatement.setString(1, login);
142+
String sqlDomainUserData = computeDomainUserDataSQLQueryFor(domainName);
143+
try (PreparedStatement userStatement = connection.prepareStatement(sqlDomainUserData)) {
144+
int i = 1;
145+
userStatement.setInt(i++, Integer.parseInt(domainId));
146+
userStatement.setString(i, login);
144147
try (ResultSet userResultSet = userStatement.executeQuery()) {
145148
if (userResultSet.next()) {
146149
user = new SilverpeasUser().withId(userResultSet.getString("id"))
@@ -165,7 +168,7 @@ private String fetchDomainNameFrom(String propFileName) {
165168
return (lastSepIndex > 0 ? propFileName.substring(lastSepIndex + 1).toLowerCase() : null);
166169
}
167170

168-
private String computeUserDataSQLQueryFor(String tableName) {
169-
return MessageFormat.format(SELECT_USER_DATA, tableName);
171+
private String computeDomainUserDataSQLQueryFor(String tableName) {
172+
return MessageFormat.format(SELECT_DOMAIN_USER_DATA, tableName);
170173
}
171174
}

src/main/java/org/silverpeas/jcr/auth/TokenAuthentication.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
public class TokenAuthentication extends AbstractAuthentication {
4747

4848
private static final String USERID_TOKEN_ATTRIBUTE = "UserID";
49+
private static final String AUTHORIZED_DOCUMENT_PATH_ATTRIBUTE = "AuthorizedDocumentPath";
4950
private static final String IDENTIFY_USER =
5051
"select id, accesslevel from st_user where state = 'VALID' and login = ? and domainId = ?";
5152
private static final Pattern TOKEN_FORMAT = Pattern.compile("[a-zA-Z0-9]{16}+");
@@ -85,16 +86,17 @@ public Principal authenticate(final Credentials credentials) throws Authenticati
8586
*/
8687
private Principal authenticate(TokenCredentials credentials) throws AuthenticationException {
8788
Principal principal = null;
88-
String token = credentials.getToken();
89-
String userID = credentials.getAttribute(USERID_TOKEN_ATTRIBUTE);
89+
final String token = credentials.getToken();
90+
final String userID = credentials.getAttribute(USERID_TOKEN_ATTRIBUTE);
9091
if (matches(token, userID)) {
91-
String[] userIdParts = fetchUserIdParts(userID);
92+
final String[] userIdParts = fetchUserIdParts(userID);
9293
if (userIdParts != null && userIdParts.length == 2) {
93-
String login = userIdParts[0];
94-
String domainId = userIdParts[1];
95-
SilverpeasUser user = identifySilverpeasUser(login, domainId);
94+
final String login = userIdParts[0];
95+
final String domainId = userIdParts[1];
96+
final SilverpeasUser user = identifySilverpeasUser(login, domainId);
9697
if (user != null) {
97-
principal = getSilverpeasUserPrincipal(user);
98+
final String authorizedDocumentPath = credentials.getAttribute(AUTHORIZED_DOCUMENT_PATH_ATTRIBUTE);
99+
principal = getSilverpeasUserPrincipal(user, authorizedDocumentPath);
98100
} else {
99101
// a TokenCredentials must match an existing user. Otherwise, it is considered as a
100102
// forbidden access

src/main/java/org/silverpeas/jcr/jaas/SilverpeasAccessManager.java

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
*/
2424
package org.silverpeas.jcr.jaas;
2525

26+
import org.apache.commons.codec.Charsets;
27+
import org.apache.jackrabbit.commons.JcrUtils;
2628
import org.apache.jackrabbit.core.id.ItemId;
2729
import org.apache.jackrabbit.core.id.NodeId;
2830
import org.apache.jackrabbit.core.id.PropertyId;
@@ -47,10 +49,13 @@
4749
import javax.jcr.SimpleCredentials;
4850
import javax.jcr.nodetype.NodeType;
4951
import javax.security.auth.Subject;
52+
import java.io.UnsupportedEncodingException;
53+
import java.net.URLDecoder;
5054
import java.text.MessageFormat;
5155
import java.util.Arrays;
5256
import java.util.List;
5357
import java.util.Set;
58+
import java.util.stream.Collectors;
5459

5560
import static org.silverpeas.jcr.JcrProperties.*;
5661

@@ -250,14 +255,14 @@ public boolean isGranted(final Path absPath, final int permissions) throws Repos
250255
Node node = session.getNode(jcrPath);
251256
if (isFolder(node)) {
252257
// only those with the correct roles can access it (for reading or modifying it).
253-
isGranted = isPathAuthorized(absPath, permissions);
258+
isGranted = isPathAuthorized(absPath);
254259
} else if (isLockedFile(node)) {
255260
// only the user owning the file can access it (for reading or updating it).
256261
isGranted = isFileAuthorized(node);
257262
} else {
258263
// it is an ordinary JCR node: everyone can read it but only those with specific roles
259264
// can update it.
260-
isGranted = permissions == Permission.READ || isPathAuthorized(absPath, permissions);
265+
isGranted = permissions == Permission.READ || isPathAuthorized(absPath);
261266
}
262267
} finally {
263268
session.logout();
@@ -345,19 +350,33 @@ public boolean canAccess(final String workspaceName) throws RepositoryException
345350
return true;
346351
}
347352

348-
private boolean isPathAuthorized(Path path, int permissions) {
349-
Set<SilverpeasUserPrincipal> principals =
353+
private boolean isPathAuthorized(Path path) {
354+
final Set<SilverpeasUserPrincipal> principals =
350355
context.getSubject().getPrincipals(SilverpeasUserPrincipal.class);
351-
Path.Element[] elements = path.getElements();
356+
final Path.Element[] elements = path.getElements();
357+
final String jcrPathToVerify = Arrays.stream(elements)
358+
.map(e -> e.getName().getLocalName())
359+
.collect(Collectors.joining("/"));
352360
for (SilverpeasUserPrincipal principal : principals) {
353-
if (principal.isAdministrator()) {
361+
if (principal.isAdministrator() || isWebdavPathAuthorized(principal, jcrPathToVerify)) {
354362
return true;
355363
}
356-
for (Path.Element element : elements) {
357-
SilverpeasUserProfile profile = principal.getUserProfile(element.getName().getLocalName());
358-
if (profile != null) {
359-
return permissions == Permission.READ || WRITING_ROLES.contains(profile.getRole());
360-
}
364+
}
365+
return false;
366+
}
367+
368+
private boolean isWebdavPathAuthorized(final SilverpeasUserPrincipal principal,
369+
final String jcrPathToVerify) {
370+
String authorizedDocumentPath = principal.getAuthorizedDocumentPath();
371+
if (authorizedDocumentPath != null) {
372+
try {
373+
authorizedDocumentPath = URLDecoder.decode(authorizedDocumentPath, Charsets.UTF_8.name());
374+
} catch (UnsupportedEncodingException ignore) {
375+
}
376+
if (authorizedDocumentPath.length() > jcrPathToVerify.length()) {
377+
return authorizedDocumentPath.contains(jcrPathToVerify);
378+
} else {
379+
return jcrPathToVerify.contains(authorizedDocumentPath);
361380
}
362381
}
363382
return false;

src/main/java/org/silverpeas/jcr/jaas/SilverpeasUserPrincipal.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
package org.silverpeas.jcr.jaas;
2525

2626
import java.security.Principal;
27-
import java.util.HashMap;
28-
import java.util.Map;
2927

3028
/**
3129
* The principals of a user in Silverpeas. They are used to check the user has enough privileges to
@@ -35,20 +33,12 @@ public class SilverpeasUserPrincipal implements Principal {
3533

3634
private String userId;
3735
private boolean administrator;
38-
private Map<String, SilverpeasUserProfile> entries;
36+
private String authorizedDocumentPath;
3937

40-
public SilverpeasUserPrincipal(String userId, boolean administrator) {
38+
public SilverpeasUserPrincipal(String userId, boolean administrator, String authorizedDocumentPath) {
4139
this.userId = userId;
4240
this.administrator = administrator;
43-
this.entries = new HashMap<>(100);
44-
}
45-
46-
public void addUserProfile(SilverpeasUserProfile aProfile) {
47-
entries.put(aProfile.getComponentId(), aProfile);
48-
}
49-
50-
public SilverpeasUserProfile getUserProfile(String componentId) {
51-
return this.entries.get(componentId);
41+
this.authorizedDocumentPath = authorizedDocumentPath;
5242
}
5343

5444
public String getUserId() {
@@ -64,4 +54,7 @@ public String getName() {
6454
return userId;
6555
}
6656

57+
public String getAuthorizedDocumentPath() {
58+
return authorizedDocumentPath;
59+
}
6760
}

src/main/java/org/silverpeas/jcr/jaas/SilverpeasUserProfile.java

Lines changed: 0 additions & 88 deletions
This file was deleted.

0 commit comments

Comments
 (0)