blob: e6571c0532875cefa41e7db22214d4bef3d71941 [file] [log] [blame]
/*
* Copyright (c) 2012, 2013, 2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Christian W. Damus (CEA LIST) - bug 399306
* Christian W. Damus (CEA LIST) - bug 418454
* Christian W. Damus (CEA LIST) - bug 399487
*/
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;
import org.eclipse.emf.cdo.internal.security.PermissionUtil;
import org.eclipse.emf.cdo.internal.security.ViewCreator;
import org.eclipse.emf.cdo.net4j.CDONet4jSession;
import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration;
import org.eclipse.emf.cdo.net4j.CDONet4jUtil;
import org.eclipse.emf.cdo.security.Access;
import org.eclipse.emf.cdo.security.Directory;
import org.eclipse.emf.cdo.security.Group;
import org.eclipse.emf.cdo.security.PatternStyle;
import org.eclipse.emf.cdo.security.Permission;
import org.eclipse.emf.cdo.security.Realm;
import org.eclipse.emf.cdo.security.Role;
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;
import org.eclipse.emf.cdo.server.ISession;
import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
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.spi.server.ObjectWriteAccessHandler;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.util.CommitException;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent;
import org.eclipse.net4j.Net4jUtil;
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.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.lifecycle.ILifecycle;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.om.monitor.OMMonitor;
import org.eclipse.net4j.util.security.IAuthenticator;
import org.eclipse.net4j.util.security.IAuthenticator2;
import org.eclipse.net4j.util.security.IPasswordCredentials;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.spi.cdo.InternalCDOSessionInvalidationEvent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author Eike Stepper
*/
public class SecurityManager extends Lifecycle implements InternalSecurityManager
{
private static final Map<IRepository, InternalSecurityManager> SECURITY_MANAGERS = new HashMap<IRepository, InternalSecurityManager>();
private static final SecurityFactory SF = SecurityFactory.eINSTANCE;
private final IListener repositoryListener = new LifecycleEventAdapter()
{
@Override
protected void onActivated(ILifecycle lifecycle)
{
init();
}
@Override
protected void onDeactivated(ILifecycle lifecycle)
{
SECURITY_MANAGERS.remove(getRepository());
SecurityManager.this.deactivate();
}
};
private final IListener sessionManagerListener = new ContainerEventAdapter<ISession>()
{
@Override
protected void onRemoved(IContainer<ISession> container, ISession session)
{
removeUserInfo(session);
}
};
private final IListener systemListener = new IListener()
{
private boolean clearUserInfos;
public void notifyEvent(IEvent event)
{
if (event instanceof InternalCDOSessionInvalidationEvent)
{
InternalCDOSessionInvalidationEvent e = (InternalCDOSessionInvalidationEvent)event;
if (e.getSecurityImpact() == CommitNotificationInfo.IMPACT_REALM)
{
clearUserInfos = true;
}
}
else if (event instanceof CDOViewInvalidationEvent)
{
if (clearUserInfos)
{
clearUserInfos();
clearUserInfos = false;
}
}
}
};
private final IAuthenticator authenticator = new Authenticator();
private final IPermissionManager permissionManager = new PermissionManager();
private final IRepository.WriteAccessHandler writeAccessHandler = new WriteAccessHandler();
private final String realmPath;
private final IManagedContainer container;
private final Map<ISession, UserInfo> userInfos = new HashMap<ISession, UserInfo>();
private final HashBag<PermissionImpl> permissionBag = new HashBag<PermissionImpl>();
private final Object commitHandlerLock = new Object();
private CommitHandler[] commitHandlers = {};
private CommitHandler2[] commitHandlers2 = {};
private PermissionImpl[] permissionArray = {};
private InternalRepository repository;
private IAcceptor acceptor;
private IConnector connector;
private CDONet4jSession systemSession;
private CDOView systemView;
private Realm realm;
private CDOID realmID;
private long lastRealmModification = CDOBranchPoint.UNSPECIFIED_DATE;
public SecurityManager(String realmPath, IManagedContainer container)
{
this.realmPath = realmPath;
this.container = container;
}
public final IManagedContainer getContainer()
{
return container;
}
public final String getRealmPath()
{
return realmPath;
}
public final IRepository getRepository()
{
return repository;
}
public void setRepository(InternalRepository repository)
{
this.repository = repository;
if (isActive())
{
init();
}
}
public Realm getRealm()
{
return realm;
}
public Role getRole(String id)
{
Role item = realm.getRole(id);
if (item == null)
{
throw new SecurityException("Role " + id + " not found");
}
return item;
}
public Group getGroup(String id)
{
Group item = realm.getGroup(id);
if (item == null)
{
throw new SecurityException("Group " + id + " not found");
}
return item;
}
public User getUser(String id)
{
User item = realm.getUser(id);
if (item == null)
{
throw new SecurityException("User " + id + " not found");
}
return item;
}
public Role addRole(final String id)
{
final Role[] result = { null };
modify(new RealmOperation()
{
public void execute(Realm realm)
{
result[0] = realm.addRole(id);
}
});
return result[0];
}
public Group addGroup(final String id)
{
final Group[] result = { null };
modify(new RealmOperation()
{
public void execute(Realm realm)
{
result[0] = realm.addGroup(id);
}
});
return result[0];
}
public User addUser(final String id)
{
final User[] result = { null };
modify(new RealmOperation()
{
public void execute(Realm realm)
{
result[0] = realm.addUser(id);
}
});
return result[0];
}
public User addUser(final String id, final String password)
{
final User[] result = { null };
modify(new RealmOperation()
{
public void execute(Realm realm)
{
UserPassword userPassword = SF.createUserPassword();
userPassword.setEncrypted(new String(password));
result[0] = realm.addUser(id);
result[0].setPassword(userPassword);
}
});
return result[0];
}
public User addUser(IPasswordCredentials credentials)
{
return addUser(credentials.getUserID(), new String(credentials.getPassword()));
}
public User setPassword(final String id, final String password)
{
final User[] result = { null };
modify(new RealmOperation()
{
public void execute(Realm realm)
{
result[0] = realm.setPassword(id, password);
}
});
return result[0];
}
public Role removeRole(final String id)
{
final Role[] result = { null };
modify(new RealmOperation()
{
public void execute(Realm realm)
{
result[0] = realm.removeRole(id);
}
});
return result[0];
}
public Group removeGroup(final String id)
{
final Group[] result = { null };
modify(new RealmOperation()
{
public void execute(Realm realm)
{
result[0] = realm.removeGroup(id);
}
});
return result[0];
}
public User removeUser(final String id)
{
final User[] result = { null };
modify(new RealmOperation()
{
public void execute(Realm realm)
{
result[0] = realm.removeUser(id);
}
});
return result[0];
}
public void read(RealmOperation operation)
{
checkReady();
operation.execute(realm);
}
public void modify(RealmOperation operation)
{
modify(operation, false);
}
public void modify(RealmOperation operation, boolean waitUntilReadable)
{
checkReady();
CDOTransaction transaction = systemSession.openTransaction();
try
{
Realm transactionRealm = transaction.getObject(realm);
operation.execute(transactionRealm);
CDOCommitInfo commit = transaction.commit();
if (waitUntilReadable)
{
systemView.waitForUpdate(commit.getTimeStamp());
}
}
catch (CommitException ex)
{
throw WrappedException.wrap(ex);
}
finally
{
transaction.close();
}
}
public CommitHandler[] getCommitHandlers()
{
return commitHandlers;
}
public CommitHandler2[] getCommitHandlers2()
{
return commitHandlers2;
}
public void addCommitHandler(CommitHandler handler)
{
checkInactive();
synchronized (commitHandlerLock)
{
commitHandlers = ArrayUtil.add(commitHandlers, handler);
if (handler instanceof CommitHandler2)
{
commitHandlers2 = ArrayUtil.add(commitHandlers2, (CommitHandler2)handler);
}
}
}
public void removeCommitHandler(CommitHandler handler)
{
checkInactive();
synchronized (commitHandlerLock)
{
commitHandlers = ArrayUtil.remove(commitHandlers, handler);
if (handler instanceof CommitHandler2)
{
commitHandlers2 = ArrayUtil.remove(commitHandlers2, (CommitHandler2)handler);
}
}
}
protected void initCommitHandlers(boolean firstTime)
{
CommitHandler[] handlers = getCommitHandlers();
for (int i = 0; i < handlers.length; i++)
{
CommitHandler handler = handlers[i];
try
{
handler.init(this, firstTime);
OM.LOG.info("Security realm handled by " + handler);
}
catch (Exception ex)
{
OM.LOG.error(ex);
}
}
}
protected void handleCommit(CommitContext commitContext, User user)
{
CommitHandler[] handlers = getCommitHandlers();
for (int i = 0; i < handlers.length; i++)
{
CommitHandler handler = handlers[i];
try
{
handler.handleCommit(this, commitContext, user);
}
catch (Exception ex)
{
OM.LOG.error(ex);
}
}
}
protected void handleCommitted(CommitContext commitContext)
{
CommitHandler2[] handlers = getCommitHandlers2();
for (int i = 0; i < handlers.length; i++)
{
CommitHandler2 handler = handlers[i];
try
{
handler.handleCommitted(this, commitContext);
}
catch (Exception ex)
{
OM.LOG.error(ex);
}
}
}
/**
* Commit-handlers can call back into the security manager to read/modify the realm
* while the security manager is in the process of initializing, so cannot strictly
* check for active state to assert that we are ready.
*/
protected void checkReady()
{
if (realm == null || systemSession == null)
{
// If I have no realm or session, I am probably inactive, so this will throw
checkActive();
}
}
protected void init()
{
if (realm != null)
{
// Already initialized
return;
}
if (repository == null)
{
// Cannot initialize
return;
}
repository.addListener(repositoryListener);
if (!LifecycleUtil.isActive(repository))
{
// Cannot initialize now
return;
}
String repositoryName = repository.getName();
String acceptorName = repositoryName + "_security";
acceptor = Net4jUtil.getAcceptor(container, "jvm", acceptorName);
connector = Net4jUtil.getConnector(container, "jvm", acceptorName);
CDONet4jSessionConfiguration config = CDONet4jUtil.createNet4jSessionConfiguration();
config.setConnector(connector);
config.setRepositoryName(repositoryName);
config.setUserID(SYSTEM_USER_ID);
systemSession = config.openNet4jSession();
systemSession.addListener(systemListener);
CDOTransaction initialTransaction = systemSession.openTransaction();
boolean firstTime = !initialTransaction.hasResource(realmPath);
if (firstTime)
{
realm = createRealm();
CDOResource resource = initialTransaction.createResource(realmPath);
resource.getContents().add(realm);
OM.LOG.info("Security realm created in " + realmPath);
}
else
{
CDOResource resource = initialTransaction.getResource(realmPath);
realm = (Realm)resource.getContents().get(0);
OM.LOG.info("Security realm loaded from " + realmPath);
}
try
{
initialTransaction.commit();
}
catch (Exception ex)
{
throw WrappedException.wrap(ex);
}
finally
{
initialTransaction.close();
}
systemView = systemSession.openView();
systemView.addListener(systemListener);
realm = systemView.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);
initCommitHandlers(firstTime);
}
protected Realm createRealm()
{
Realm realm = SF.createRealm("Security Realm");
realm.setDefaultRoleDirectory(addDirectory(realm, Directory.ROLES));
realm.setDefaultGroupDirectory(addDirectory(realm, Directory.GROUPS));
realm.setDefaultUserDirectory(addDirectory(realm, Directory.USERS));
// Create roles
Role allReaderRole = realm.addRole(Role.ALL_OBJECTS_READER);
allReaderRole.getPermissions()
.add(SF.createFilterPermission(Access.READ, SF.createResourceFilter(".*", PatternStyle.REGEX)));
Role allWriterRole = realm.addRole(Role.ALL_OBJECTS_WRITER);
allWriterRole.getPermissions()
.add(SF.createFilterPermission(Access.WRITE, SF.createResourceFilter(".*", PatternStyle.REGEX)));
Role treeReaderRole = realm.addRole(Role.RESOURCE_TREE_READER);
treeReaderRole.getPermissions()
.add(SF.createFilterPermission(Access.READ, SF.createPackageFilter(EresourcePackage.eINSTANCE)));
Role treeWriterRole = realm.addRole(Role.RESOURCE_TREE_WRITER);
treeWriterRole.getPermissions()
.add(SF.createFilterPermission(Access.WRITE, SF.createPackageFilter(EresourcePackage.eINSTANCE)));
Role adminRole = realm.addRole(Role.ADMINISTRATION);
adminRole.getPermissions()
.add(SF.createFilterPermission(Access.WRITE, SF.createResourceFilter(realmPath, PatternStyle.EXACT, false)));
adminRole.getPermissions()
.add(SF.createFilterPermission(Access.READ, SF.createResourceFilter(realmPath, PatternStyle.EXACT, true)));
// Create groups
Group adminsGroup = realm.addGroup(Group.ADMINISTRATORS);
adminsGroup.getRoles().add(adminRole);
realm.addGroup(Directory.USERS);
// Create users
User adminUser = realm.addUser(User.ADMINISTRATOR, "0000");
adminUser.getGroups().add(adminsGroup);
return realm;
}
protected Directory addDirectory(Realm realm, String name)
{
Directory directory = SF.createDirectory(name);
realm.getItems().add(directory);
return directory;
}
protected CDOPermission convertPermission(Access permission)
{
if (permission != null)
{
switch (permission)
{
case READ:
return CDOPermission.READ;
case WRITE:
return CDOPermission.WRITE;
}
}
return CDOPermission.NONE;
}
protected CDOPermission authorize(CDORevision revision, CDORevisionProvider revisionProvider,
CDOBranchPoint securityContext, ISession session, Access defaultAccess, Permission[] permissions)
{
if (lastRealmModification != CDOBranchPoint.UNSPECIFIED_DATE)
{
systemView.waitForUpdate(lastRealmModification);
lastRealmModification = CDOBranchPoint.UNSPECIFIED_DATE;
}
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(defaultAccess);
if (result == CDOPermission.WRITE)
{
return result;
}
for (int i = 0; i < permissions.length; i++)
{
Permission permission = permissions[i];
CDOPermission p = convertPermission(permission.getAccess());
if (p.ordinal() <= result.ordinal())
{
// Avoid expensive calls to Permission.isApplicable() if the permission wouldn't increase
continue;
}
if (permission.isApplicable(revision, revisionProvider, securityContext))
{
result = p;
if (result == CDOPermission.WRITE)
{
return result;
}
}
}
return result;
}
finally
{
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;
}
protected void clearUserInfos()
{
synchronized (userInfos)
{
// System.out.println("clearUserInfos()");
userInfos.clear();
permissionBag.clear();
permissionArray = null;
}
}
protected final boolean isAdministrator(User user)
{
// An administrator is one that has write permission on the realm resource
Realm realm = getRealm();
if (realm != null)
{
// Can't be an administrator if there isn't a realm
CDORevision revision = realm.cdoRevision();
CDORevisionProvider revisionProvider = realm.cdoView();
CDOBranchPoint securityContext = realm.cdoView();
for (Permission permission : user.getAllPermissions())
{
if (permission.getAccess() == Access.WRITE
&& permission.isApplicable(revision, revisionProvider, securityContext))
{
return true;
}
}
}
return false;
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
init();
}
@Override
protected void doDeactivate() throws Exception
{
clearUserInfos();
realm = null;
realmID = null;
systemSession.close();
systemSession = null;
systemView = null;
connector.close();
connector = null;
acceptor.close();
acceptor = null;
super.doDeactivate();
}
public static InternalSecurityManager get(IRepository repository)
{
return SECURITY_MANAGERS.get(repository);
}
/**
* @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 IAuthenticator2
{
public void authenticate(String userID, char[] password) throws SecurityException
{
User user = getUser(userID);
UserPassword userPassword = user.getPassword();
if (userPassword != null)
{
String encrypted = userPassword.getEncrypted();
if (!Arrays.equals(password, encrypted == null ? null : encrypted.toCharArray()))
{
throw new SecurityException("Access denied"); //$NON-NLS-1$
}
}
}
public void updatePassword(String userID, char[] oldPassword, char[] newPassword)
{
authenticate(userID, oldPassword);
setPassword(userID, new String(newPassword));
}
public void resetPassword(String adminID, char[] adminPassword, String userID, char[] newPassword)
{
authenticate(adminID, adminPassword);
User admin = getUser(adminID);
if (!SecurityManager.this.isAdministrator(admin))
{
throw new SecurityException("Password reset requires administrator privilege"); //$NON-NLS-1$
}
setPassword(userID, new String(newPassword));
}
public boolean isAdministrator(String userID)
{
Realm realm = getRealm();
if (realm != null)
{
// Can't be an administrator if there isn't a realm
// (but then where did we get the user ID?)
User user = realm.getUser(userID);
return user != null && SecurityManager.this.isAdministrator(user);
}
return false;
}
}
/**
* @author Eike Stepper
*/
private final class PermissionManager implements IPermissionManager
{
@Deprecated
public CDOPermission getPermission(CDORevision revision, CDOBranchPoint securityContext, String userID)
{
throw new UnsupportedOperationException();
}
public CDOPermission getPermission(CDORevision revision, final CDOBranchPoint securityContext,
final ISession session)
{
String userID = session.getUserID();
if (SYSTEM_USER_ID.equals(userID))
{
return CDOPermission.WRITE;
}
if (revision.getEClass() == SecurityPackage.Literals.USER_PASSWORD)
{
return CDOPermission.NONE;
}
InternalCDORevisionManager revisionManager = repository.getRevisionManager();
CDORevisionProvider revisionProvider = new ManagedRevisionProvider(revisionManager, securityContext);
PermissionUtil.initViewCreation(new ViewCreator()
{
public CDOView createView(CDORevisionProvider revisionProvider)
{
return CDOServerUtil.openView(session, securityContext, revisionProvider);
}
});
try
{
CDOPermission permission = authorize(revision, revisionProvider, securityContext, session, null, null);
// System.out.println("Loading from " + session + ": " + permission + " --> " + revision);
return permission;
}
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;
}
}
/**
* @author Eike Stepper
*/
private final class WriteAccessHandler implements IRepository.WriteAccessHandler
{
private final IRepository.WriteAccessHandler realmValidationHandler = new RealmValidationHandler();
public void handleTransactionBeforeCommitting(ITransaction transaction, final CommitContext commitContext,
OMMonitor monitor) throws RuntimeException
{
doHandleTransactionBeforeCommitting(transaction, commitContext, monitor);
if (commitContext.getSecurityImpact() == CommitNotificationInfo.IMPACT_REALM)
{
// Validate changes to the realm
realmValidationHandler.handleTransactionBeforeCommitting(transaction, commitContext, monitor);
}
}
protected void doHandleTransactionBeforeCommitting(ITransaction transaction, final CommitContext commitContext,
OMMonitor monitor) throws RuntimeException
{
if (transaction.getSessionID() == systemSession.getSessionID())
{
// Access through ISecurityManager.modify(RealmOperation)
handleCommit(commitContext, null);
((InternalCommitContext)commitContext).setSecurityImpact(CommitNotificationInfo.IMPACT_REALM, null);
return;
}
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)
{
return CDOServerUtil.openView(commitContext);
}
});
try
{
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();
}
}
public void handleTransactionAfterCommitted(ITransaction transaction, final CommitContext commitContext,
OMMonitor monitor)
{
if (commitContext.getSecurityImpact() == CommitNotificationInfo.IMPACT_REALM)
{
lastRealmModification = commitContext.getBranchPoint().getTimeStamp();
}
handleCommitted(commitContext);
}
}
/**
* A write-access handler that checks changes about to be committed to the security realm
* against its well-formedness rules, and rejects the commit if there are any integrity
* errors.
*
* @author Christian W. Damus (CEA LIST)
*/
private final class RealmValidationHandler extends ObjectWriteAccessHandler
{
private final EValidator realmValidator = EValidator.Registry.INSTANCE.getEValidator(SecurityPackage.eINSTANCE);
@Override
protected void handleTransactionBeforeCommitting(OMMonitor monitor) throws RuntimeException
{
final BasicDiagnostic diagnostic = new BasicDiagnostic();
final Map<Object, Object> context = createValidationContext();
boolean realmChecked = false;
for (EObject object : getDirtyObjects())
{
if (object.eClass().getEPackage() == SecurityPackage.eINSTANCE)
{
validate(object, diagnostic, context);
realmChecked = object instanceof Realm;
}
}
for (EObject object : getNewObjects())
{
if (object.eClass().getEPackage() == SecurityPackage.eINSTANCE)
{
validate(object, diagnostic, context);
// The realm cannot be new
}
}
if (!realmChecked)
{
// Check it, because it has some wide-ranging integrity constraints
validate(getView().getObject(realmID), diagnostic, context);
}
}
protected Map<Object, Object> createValidationContext()
{
Map<Object, Object> result = new java.util.HashMap<Object, Object>();
final CommitContext commitContext = getCommitContext();
// Supply the revision-provider and branch point required by realm validation
result.put(CDORevisionProvider.class, commitContext);
result.put(CDOBranchPoint.class, commitContext.getBranchPoint());
return result;
}
protected void validate(EObject object, DiagnosticChain diagnostics, Map<Object, Object> context)
{
realmValidator.validate(object, diagnostics, context);
Diagnostic error = getError(diagnostics);
if (error != null)
{
throw new TransactionValidationException("Security realm integrity violation: " + error.getMessage());
}
}
protected Diagnostic getError(DiagnosticChain diagnostics)
{
Diagnostic diagnostic = (Diagnostic)diagnostics;
if (diagnostic.getSeverity() >= Diagnostic.ERROR)
{
for (Diagnostic child : diagnostic.getChildren())
{
if (child.getSeverity() >= Diagnostic.ERROR)
{
return child;
}
}
}
return null;
}
}
}