diff options
author | Eike Stepper | 2013-09-27 09:28:33 +0000 |
---|---|---|
committer | Eike Stepper | 2013-09-29 09:40:31 +0000 |
commit | 48c07bf1f2acc05313f6f4b3bfaee8db208b1d66 (patch) | |
tree | 482a121fa8d603ac3e0a3016a0ee52cff685b66b /plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf | |
parent | 5f0ddac06df136a2a0b7c46fe33400386da92a53 (diff) | |
download | cdo-48c07bf1f2acc05313f6f4b3bfaee8db208b1d66.tar.gz cdo-48c07bf1f2acc05313f6f4b3bfaee8db208b1d66.tar.xz cdo-48c07bf1f2acc05313f6f4b3bfaee8db208b1d66.zip |
[418267] [Security] Cached permissions are not always properly updated
after commits
https://bugs.eclipse.org/bugs/show_bug.cgi?id=418267
Diffstat (limited to 'plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf')
-rw-r--r-- | plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityManager.java | 361 |
1 files changed, 287 insertions, 74 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityManager.java b/plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityManager.java index 5a2d5715b9..0ef4eaf529 100644 --- a/plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityManager.java +++ b/plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityManager.java @@ -12,8 +12,12 @@ package org.eclipse.emf.cdo.server.internal.security; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.common.security.CDOPermission; import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.eresource.EresourcePackage; @@ -33,6 +37,8 @@ import org.eclipse.emf.cdo.security.SecurityFactory; import org.eclipse.emf.cdo.security.SecurityPackage; import org.eclipse.emf.cdo.security.User; import org.eclipse.emf.cdo.security.UserPassword; +import org.eclipse.emf.cdo.security.impl.PermissionImpl; +import org.eclipse.emf.cdo.security.impl.PermissionImpl.CommitImpactContext; import org.eclipse.emf.cdo.server.CDOServerUtil; import org.eclipse.emf.cdo.server.IPermissionManager; import org.eclipse.emf.cdo.server.IRepository; @@ -42,8 +48,10 @@ import org.eclipse.emf.cdo.server.ITransaction; import org.eclipse.emf.cdo.server.internal.security.bundle.OM; import org.eclipse.emf.cdo.server.spi.security.InternalSecurityManager; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; import org.eclipse.emf.cdo.spi.common.revision.ManagedRevisionProvider; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.emf.cdo.spi.server.InternalSessionManager; import org.eclipse.emf.cdo.transaction.CDOTransaction; @@ -55,6 +63,9 @@ import org.eclipse.net4j.acceptor.IAcceptor; import org.eclipse.net4j.connector.IConnector; import org.eclipse.net4j.util.ArrayUtil; import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.collection.HashBag; +import org.eclipse.net4j.util.container.ContainerEventAdapter; +import org.eclipse.net4j.util.container.IContainer; import org.eclipse.net4j.util.container.IManagedContainer; import org.eclipse.net4j.util.event.IListener; import org.eclipse.net4j.util.lifecycle.ILifecycle; @@ -68,9 +79,10 @@ import org.eclipse.net4j.util.security.IPasswordCredentials; import org.eclipse.emf.common.util.EList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * @author Eike Stepper @@ -97,6 +109,15 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage } }; + private final IListener sessionManagerListener = new ContainerEventAdapter<ISession>() + { + @Override + protected void onRemoved(IContainer<ISession> container, ISession session) + { + removeUserInfo(session); + } + }; + private final IAuthenticator authenticator = new Authenticator(); private final IPermissionManager permissionManager = new PermissionManager(); @@ -107,7 +128,9 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage private final IManagedContainer container; - private final Map<String, User> users = Collections.synchronizedMap(new HashMap<String, User>()); + private final Map<ISession, UserInfo> userInfos = new HashMap<ISession, UserInfo>(); + + private final HashBag<PermissionImpl> permissionBag = new HashBag<PermissionImpl>(); private final Object commitHandlerLock = new Object(); @@ -115,18 +138,22 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage private CommitHandler2[] commitHandlers2 = {}; + private PermissionImpl[] permissionArray = {}; + private InternalRepository repository; private IAcceptor acceptor; private IConnector connector; - private CDONet4jSession session; + private CDONet4jSession systemSession; private CDOView view; private Realm realm; + private CDOID realmID; + public SecurityManager(String realmPath, IManagedContainer container) { this.realmPath = realmPath; @@ -186,16 +213,10 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage public User getUser(String id) { - User item = users.get(id); + User item = realm.getUser(id); if (item == null) { - item = realm.getUser(id); - if (item == null) - { - throw new SecurityException("User " + id + " not found"); - } - - users.put(id, item); + throw new SecurityException("User " + id + " not found"); } return item; @@ -336,7 +357,7 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage public void modify(RealmOperation operation, boolean waitUntilReadable) { checkActive(); - CDOTransaction transaction = session.openTransaction(); + CDOTransaction transaction = systemSession.openTransaction(); try { @@ -483,8 +504,8 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage config.setRepositoryName(repositoryName); config.setUserID(SYSTEM_USER_ID); - session = config.openNet4jSession(); - CDOTransaction transaction = session.openTransaction(); + systemSession = config.openNet4jSession(); + CDOTransaction transaction = systemSession.openTransaction(); boolean firstTime = !transaction.hasResource(realmPath); if (firstTime) @@ -516,12 +537,14 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage transaction.close(); } - view = session.openView(); + view = systemSession.openView(); realm = view.getObject(realm); + realmID = realm.cdoID(); InternalSessionManager sessionManager = repository.getSessionManager(); sessionManager.setAuthenticator(authenticator); sessionManager.setPermissionManager(permissionManager); + sessionManager.addListener(sessionManagerListener); repository.addHandler(writeAccessHandler); SECURITY_MANAGERS.put(repository, this); @@ -531,29 +554,29 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage protected Realm createRealm() { Realm realm = SF.createRealm("Security Realm"); - realm.setDefaultRoleDirectory(addDirectory(realm, "Roles")); - realm.setDefaultGroupDirectory(addDirectory(realm, "Groups")); - realm.setDefaultUserDirectory(addDirectory(realm, "Users")); + realm.setDefaultRoleDirectory(addDirectory(realm, Directory.ROLES)); + realm.setDefaultGroupDirectory(addDirectory(realm, Directory.GROUPS)); + realm.setDefaultUserDirectory(addDirectory(realm, Directory.USERS)); // Create roles - Role allReaderRole = realm.addRole("All Objects Reader"); + Role allReaderRole = realm.addRole(Role.ALL_OBJECTS_READER); allReaderRole.getPermissions().add( SF.createFilterPermission(Access.READ, SF.createResourceFilter(".*", PatternStyle.REGEX))); - Role allWriterRole = realm.addRole("All Objects Writer"); + Role allWriterRole = realm.addRole(Role.ALL_OBJECTS_WRITER); allWriterRole.getPermissions().add( SF.createFilterPermission(Access.WRITE, SF.createResourceFilter(".*", PatternStyle.REGEX))); - Role treeReaderRole = realm.addRole("Resource Tree Reader"); + Role treeReaderRole = realm.addRole(Role.RESOURCE_TREE_READER); treeReaderRole.getPermissions().add( SF.createFilterPermission(Access.READ, SF.createPackageFilter(EresourcePackage.eINSTANCE))); - Role treeWriterRole = realm.addRole("Resource Tree Writer"); + Role treeWriterRole = realm.addRole(Role.RESOURCE_TREE_WRITER); treeWriterRole.getPermissions().add( SF.createFilterPermission(Access.WRITE, SF.createPackageFilter(EresourcePackage.eINSTANCE))); - Role adminRole = realm.addRole("Administration"); + Role adminRole = realm.addRole(Role.ADMINISTRATION); adminRole.getPermissions().add( SF.createFilterPermission(Access.WRITE, SF.createResourceFilter(realmPath, PatternStyle.EXACT, false))); adminRole.getPermissions().add( @@ -561,14 +584,14 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage // Create groups - Group adminsGroup = realm.addGroup("Administrators"); + Group adminsGroup = realm.addGroup(Group.ADMINISTRATORS); adminsGroup.getRoles().add(adminRole); - realm.addGroup("Users"); + realm.addGroup(Directory.USERS); // Create users - User adminUser = realm.addUser("Administrator", "0000"); + User adminUser = realm.addUser(User.ADMINISTRATOR, "0000"); adminUser.getGroups().add(adminsGroup); return realm; @@ -598,22 +621,33 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage return CDOPermission.NONE; } - protected CDOPermission getPermission(CDORevision revision, CDORevisionProvider revisionProvider, - CDOBranchPoint securityContext, ISession session, User user) + protected CDOPermission authorize(CDORevision revision, CDORevisionProvider revisionProvider, + CDOBranchPoint securityContext, ISession session, Access defaultAccess, Permission[] permissions) { - PermissionUtil.setUser(user.getId()); + boolean setUser = defaultAccess == null; + if (setUser) + { + UserInfo userInfo = getUserInfo(session); + User user = userInfo.getUser(); + + defaultAccess = user.getDefaultAccess(); + permissions = userInfo.getPermissions(); + + PermissionUtil.setUser(user.getId()); + } try { - CDOPermission result = convertPermission(user.getDefaultAccess()); + CDOPermission result = convertPermission(defaultAccess); if (result == CDOPermission.WRITE) { return result; } - EList<Permission> allPermissions = user.getAllPermissions(); - for (Permission permission : allPermissions) + for (int i = 0; i < permissions.length; i++) { + Permission permission = permissions[i]; + CDOPermission p = convertPermission(permission.getAccess()); if (p.ordinal() <= result.ordinal()) { @@ -635,8 +669,75 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage } finally { - PermissionUtil.setUser(null); + if (setUser) + { + PermissionUtil.setUser(null); + } + } + } + + protected UserInfo getUserInfo(ISession session) + { + UserInfo userInfo; + synchronized (userInfos) + { + userInfo = userInfos.get(session); + } + + if (userInfo == null) + { + userInfo = addUserInfo(session); + } + + return userInfo; + } + + protected UserInfo addUserInfo(ISession session) + { + String userID = session.getUserID(); + User user = getUser(userID); + UserInfo userInfo = new UserInfo(user); + + synchronized (userInfos) + { + userInfos.put(session, userInfo); + + Permission[] permissions = userInfo.getPermissions(); + for (int i = 0; i < permissions.length; i++) + { + Permission permission = permissions[i]; + permissionBag.add((PermissionImpl)permission); + } + + // Atomic update + permissionArray = permissionBag.toArray(new PermissionImpl[permissionBag.size()]); } + + return userInfo; + } + + protected UserInfo removeUserInfo(ISession session) + { + UserInfo userInfo; + synchronized (userInfos) + { + userInfo = userInfos.remove(session); + + if (userInfo != null) + { + Permission[] permissions = userInfo.getPermissions(); + for (int i = 0; i < permissions.length; i++) + { + Permission permission = permissions[i]; + permissionBag.remove(permission); + } + + // Atomic update + permissionArray = permissionBag.toArray(new PermissionImpl[permissionBag.size()]); + } + } + + return userInfo; } @Override @@ -649,11 +750,15 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage @Override protected void doDeactivate() throws Exception { - users.clear(); + userInfos.clear(); + permissionBag.clear(); + permissionArray = null; + realm = null; + realmID = null; - session.close(); - session = null; + systemSession.close(); + systemSession = null; view = null; connector.close(); @@ -673,6 +778,33 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage /** * @author Eike Stepper */ + private static final class UserInfo + { + private final User user; + + private final Permission[] permissions; + + public UserInfo(User user) + { + this.user = user; + EList<Permission> allPermissions = user.getAllPermissions(); + permissions = allPermissions.toArray(new Permission[allPermissions.size()]); + } + + public User getUser() + { + return user; + } + + public Permission[] getPermissions() + { + return permissions; + } + } + + /** + * @author Eike Stepper + */ private final class Authenticator implements IAuthenticator { public void authenticate(String userID, char[] password) throws SecurityException @@ -696,38 +828,26 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage */ private final class PermissionManager implements IPermissionManager { - public CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext, ISession session) + @Deprecated + public CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext, String userID) { - String userID = session.getUserID(); - if (SYSTEM_USER_ID.equals(userID)) - { - return CDOPermission.WRITE; - } - - return doGetPermission(revision, securityContext, session, userID); + throw new UnsupportedOperationException(); } - @Deprecated - public CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext, String userID) + public CDOPermission getPermission(CDORevision revision, final CDOBranchPoint securityContext, + final ISession session) { + String userID = session.getUserID(); if (SYSTEM_USER_ID.equals(userID)) { return CDOPermission.WRITE; } - return doGetPermission(revision, securityContext, null, userID); - } - - private CDOPermission doGetPermission(CDORevision revision, final CDOBranchPoint securityContext, - final ISession session, String userID) - { if (revision.getEClass() == SecurityPackage.Literals.USER_PASSWORD) { return CDOPermission.NONE; } - User user = getUser(userID); - InternalCDORevisionManager revisionManager = repository.getRevisionManager(); CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, securityContext); @@ -741,13 +861,35 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage try { - return SecurityManager.this.getPermission(revision, revisionProvider, securityContext, session, user); + return authorize(revision, revisionProvider, securityContext, session, null, null); } finally { PermissionUtil.doneViewCreation(); } } + + public boolean hasAnyRule(ISession session, Set<? extends Object> rules) + { + String userID = session.getUserID(); + if (SYSTEM_USER_ID.equals(userID)) + { + return false; + } + + UserInfo userInfo = getUserInfo(session); + Permission[] userPermissions = userInfo.getPermissions(); + for (int i = 0; i < userPermissions.length; i++) + { + Permission userPermission = userPermissions[i]; + if (rules.contains(userPermission)) + { + return true; + } + } + + return false; + } } /** @@ -758,19 +900,20 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage public void handleTransactionBeforeCommitting(ITransaction transaction, final CommitContext commitContext, OMMonitor monitor) throws RuntimeException { - if (transaction.getSessionID() == session.getSessionID()) + if (transaction.getSessionID() == systemSession.getSessionID()) { // Access through ISecurityManager.modify(RealmOperation) handleCommit(commitContext, null); + ((InternalCommitContext)commitContext).setSecurityImpact(CommitNotificationInfo.IMPACT_REALM, null); return; } - CDOBranchPoint securityContext = commitContext.getBranchPoint(); - String userID = commitContext.getUserID(); - User user = getUser(userID); + UserInfo userInfo = getUserInfo(transaction.getSession()); + User user = userInfo.getUser(); handleCommit(commitContext, user); + PermissionUtil.setUser(user.getId()); PermissionUtil.initViewCreation(new ViewCreator() { public CDOView createView(CDORevisionProvider revisionProvider) @@ -781,28 +924,98 @@ public class SecurityManager extends Lifecycle implements InternalSecurityManage try { - permissionRevisionsBeforeCommitting(commitContext, securityContext, user, commitContext.getDirtyObjects()); + CDOBranchPoint securityContext = commitContext.getBranchPoint(); + ISession session = transaction.getSession(); + + Access userDefaultAccess = user.getDefaultAccess(); + Permission[] userPermissions = userInfo.getPermissions(); + + final InternalCDORevision[] revisions = commitContext.getDirtyObjects(); + final InternalCDORevisionDelta[] revisionDeltas = commitContext.getDirtyObjectDeltas(); + + // Check permissions on the commit changes and detect realm modifications + byte securityImpact = CommitNotificationInfo.IMPACT_NONE; + for (int i = 0; i < revisions.length; i++) + { + InternalCDORevision revision = revisions[i]; + CDOPermission permission = authorize(revision, commitContext, securityContext, session, userDefaultAccess, + userPermissions); + + if (permission != CDOPermission.WRITE) + { + throw new SecurityException("User " + commitContext.getUserID() + " is not allowed to write to " + revision); + } + + if (securityImpact != CommitNotificationInfo.IMPACT_REALM) + { + InternalCDORevisionDelta revisionDelta = revisionDeltas[i]; + if (CDORevisionUtil.isContained(revisionDelta.getID(), realmID, transaction)) // Use "before commit" state + { + securityImpact = CommitNotificationInfo.IMPACT_REALM; + } + } + } + + // Determine permissions that are impacted by the commit changes + Set<Permission> impactedRules = null; + if (securityImpact != CommitNotificationInfo.IMPACT_REALM) + { + PermissionImpl[] assignedPermissions = permissionArray; // Thread-safe + if (assignedPermissions.length != 0) + { + CommitImpactContext commitImpactContext = new PermissionImpl.CommitImpactContext() + { + public CDORevision[] getNewObjects() + { + return commitContext.getNewObjects(); + } + + public CDORevision[] getDirtyObjects() + { + return revisions; + } + + public CDORevisionDelta[] getDirtyObjectDeltas() + { + return revisionDeltas; + } + + public CDOID[] getDetachedObjects() + { + return commitContext.getDetachedObjects(); + } + }; + + for (int i = 0; i < assignedPermissions.length; i++) + { + PermissionImpl permission = assignedPermissions[i]; + if (permission.isImpacted(commitImpactContext)) + { + if (impactedRules == null) + { + impactedRules = new HashSet<Permission>(); + } + + impactedRules.add(permission); + } + } + + if (impactedRules != null) + { + securityImpact = CommitNotificationInfo.IMPACT_PERMISSIONS; + } + } + } + + ((InternalCommitContext)commitContext).setSecurityImpact(securityImpact, impactedRules); } finally { + PermissionUtil.setUser(null); PermissionUtil.doneViewCreation(); } } - private void permissionRevisionsBeforeCommitting(CommitContext commitContext, CDOBranchPoint securityContext, - User user, InternalCDORevision[] revisions) - { - ISession session = commitContext.getTransaction().getSession(); - for (InternalCDORevision revision : revisions) - { - CDOPermission permission = getPermission(revision, commitContext, securityContext, session, user); - if (permission != CDOPermission.WRITE) - { - throw new SecurityException("User " + user + " is not allowed to write to " + revision); - } - } - } - public void handleTransactionAfterCommitted(ITransaction transaction, final CommitContext commitContext, OMMonitor monitor) { |