/* * Copyright (c) 2012, 2013, 2015, 2016 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 * Laurent Redor (Obeo) - bug 501607 */ 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.concurrent.TimeoutRuntimeException; 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) { if (!systemView.waitForUpdate(commit.getTimeStamp(), 10000)) { throw new TimeoutRuntimeException(); } } } 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.options().setGeneratedPackageEmulationEnabled(true); 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) { if (!systemView.waitForUpdate(lastRealmModification, 10000L)) { throw new TimeoutRuntimeException(); } 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) { CDOView view = CDOServerUtil.openView(session, securityContext, revisionProvider); view.getSession().options().setGeneratedPackageEmulationEnabled(true); return view; } }); 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; } } }