All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mindoo.domino.jna.NotesACL Maven / Gradle / Ivy

There is a newer version: 0.9.53
Show newest version
package com.mindoo.domino.jna;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.mindoo.domino.jna.constants.AclFlag;
import com.mindoo.domino.jna.constants.AclLevel;
import com.mindoo.domino.jna.errors.NotesError;
import com.mindoo.domino.jna.errors.NotesErrorUtils;
import com.mindoo.domino.jna.gc.IAllocatedMemory;
import com.mindoo.domino.jna.gc.NotesGC;
import com.mindoo.domino.jna.internal.DisposableMemory;
import com.mindoo.domino.jna.internal.Mem32;
import com.mindoo.domino.jna.internal.Mem64;
import com.mindoo.domino.jna.internal.NotesCallbacks.ACLENTRYENUMFUNC;
import com.mindoo.domino.jna.internal.NotesConstants;
import com.mindoo.domino.jna.internal.NotesNativeAPI;
import com.mindoo.domino.jna.internal.NotesNativeAPI32;
import com.mindoo.domino.jna.internal.NotesNativeAPI64;
import com.mindoo.domino.jna.internal.Win32NotesCallbacks;
import com.mindoo.domino.jna.utils.ListUtil;
import com.mindoo.domino.jna.utils.NotesNamingUtils;
import com.mindoo.domino.jna.utils.NotesStringUtils;
import com.mindoo.domino.jna.utils.PlatformUtils;
import com.mindoo.domino.jna.utils.StringUtil;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.LongByReference;
import com.sun.jna.ptr.ShortByReference;

/**
 * Access control list of a {@link NotesDatabase}
 * 
 * @author Karsten Lehmann
 */
public class NotesACL implements IAllocatedMemory {
	private NotesDatabase m_parentDb;
	private long m_hACL64;
	private int m_hACL32;
	
	NotesACL(NotesDatabase parentDb, long hACL) {
		if (!PlatformUtils.is64Bit())
			throw new IllegalStateException("Constructor is 64bit only");
		m_parentDb = parentDb;
		m_hACL64 = hACL;
	}
	
	NotesACL(NotesDatabase parentDb, int hACL) {
		if (PlatformUtils.is64Bit())
			throw new IllegalStateException("Constructor is 32bit only");
		m_parentDb = parentDb;
		m_hACL32 = hACL;
	}
	
	public NotesDatabase getParentDatabase() {
		return m_parentDb;
	}
	
	@Override
	public void free() {
		if (isFreed())
			return;

		if (PlatformUtils.is64Bit()) {
			NotesGC.__memoryBeeingFreed(this);
			short result = Mem64.OSMemFree(m_hACL64);
			NotesErrorUtils.checkResult(result);
			m_hACL64=0;
		}
		else {
			NotesGC.__memoryBeeingFreed(this);
			short result = Mem32.OSMemFree(m_hACL32);
			NotesErrorUtils.checkResult(result);
			m_hACL32=0;
		}
	}

	@Override
	public boolean isFreed() {
		if (PlatformUtils.is64Bit()) {
			return m_hACL64 == 0;
		}
		else {
			return m_hACL32 == 0;
		}
	}

	@Override
	public int getHandle32() {
		return m_hACL32;
	}

	@Override
	public long getHandle64() {
		return m_hACL64;
	}

	/**
	 * Change the name of the administration server for the access control list.
	 * 
	 * @param server server, either in abbreviated or canonical format
	 */
	public void setAdminServer(String server) {
		checkHandle();
		
		Memory serverCanonicalMem = NotesStringUtils.toLMBCS(NotesNamingUtils.toCanonicalName(server), true);
		short result;
		if (PlatformUtils.is64Bit()) {
			result = NotesNativeAPI64.get().ACLSetAdminServer(m_hACL64, serverCanonicalMem);
		}
		else {
			result = NotesNativeAPI32.get().ACLSetAdminServer(m_hACL32, serverCanonicalMem);
		}
		NotesErrorUtils.checkResult(result);
	}
	
	/**
	 * This function stores the access control list in the parent database.
*/ public void save() { checkHandle(); if (m_parentDb.isRecycled()) throw new NotesError(0, "Parent database already recycled"); short result; if (PlatformUtils.is64Bit()) { result = NotesNativeAPI64.get().NSFDbStoreACL(m_parentDb.getHandle64(), m_hACL64, 0, (short) 0); } else { result = NotesNativeAPI32.get().NSFDbStoreACL(m_parentDb.getHandle32(), m_hACL32, 0, (short) 0); } NotesErrorUtils.checkResult(result); } /** * Checks if the database is already recycled */ private void checkHandle() { if (PlatformUtils.is64Bit()) { if (m_hACL64==0) throw new NotesError(0, "Memory already freed"); NotesGC.__b64_checkValidMemHandle(getClass(), m_hACL64); } else { if (m_hACL32==0) throw new NotesError(0, "Memory already freed"); NotesGC.__b32_checkValidMemHandle(getClass(), m_hACL32); } } /** * Looks up the access level for a user and his groups * * @param userName username, either canonical or abbreviated * @return acl access info, with access level, flags and roles */ public NotesACLAccess lookupAccess(String userName) { NotesNamesList namesList = NotesNamingUtils.buildNamesList(m_parentDb.getServer(), userName); try { return lookupAccess(namesList); } finally { namesList.free(); } } /** * Looks up the access level for a {@link NotesNamesList} * * @param namesList names list for a user * @return acl access info, with access level, flags and roles */ public NotesACLAccess lookupAccess(NotesNamesList namesList) { if (namesList.isFreed()) throw new NotesError(0, "Nameslist is already freed"); ShortByReference retAccessLevel = new ShortByReference(); Memory retPrivileges = new Memory(10); ShortByReference retAccessFlags = new ShortByReference(); short result; if (PlatformUtils.is64Bit()) { LongByReference rethPrivNames = new LongByReference(); long hNamesList = namesList.getHandle64(); Pointer pNamesList = Mem64.OSLockObject(hNamesList); try { result = NotesNativeAPI64.get().ACLLookupAccess(m_hACL64, pNamesList, retAccessLevel, retPrivileges, retAccessFlags, rethPrivNames); NotesErrorUtils.checkResult(result); long hPrivNames = rethPrivNames.getValue(); List roles; if (hPrivNames==0) roles = Collections.emptyList(); else { Pointer pPrivNames = Mem64.OSLockObject(hPrivNames); ShortByReference retTextLength = new ShortByReference(); Memory retTextPointer = new Memory(Native.POINTER_SIZE); try { int numEntriesAsInt = (int) (NotesNativeAPI.get().ListGetNumEntries(pPrivNames, 0) & 0xffff); roles = new ArrayList(numEntriesAsInt); for (int i=0; i retFlags = EnumSet.noneOf(AclFlag.class); for (AclFlag currFlag : AclFlag.values()) { if ((iAccessFlag & currFlag.getValue()) == currFlag.getValue()) { retFlags.add(currFlag); } } NotesACLAccess access = new NotesACLAccess(accessLevel, roles, retFlags); return access; } finally { Mem64.OSUnlockObject(hNamesList); } } else { IntByReference rethPrivNames = new IntByReference(); int hNamesList = namesList.getHandle32(); Pointer pNamesList = Mem32.OSLockObject(hNamesList); try { result = NotesNativeAPI32.get().ACLLookupAccess(m_hACL32, pNamesList, retAccessLevel, retPrivileges, retAccessFlags, rethPrivNames); NotesErrorUtils.checkResult(result); int hPrivNames = rethPrivNames.getValue(); List roles; if (hPrivNames==0) roles = Collections.emptyList(); else { Pointer pPrivNames = Mem32.OSLockObject(hPrivNames); ShortByReference retTextLength = new ShortByReference(); Memory retTextPointer = new Memory(Native.POINTER_SIZE); try { int numEntriesAsInt = (int) (NotesNativeAPI.get().ListGetNumEntries(pPrivNames, 0) & 0xffff); roles = new ArrayList(numEntriesAsInt); for (int i=0; i retFlags = EnumSet.noneOf(AclFlag.class); for (AclFlag currFlag : AclFlag.values()) { if ((iAccessFlag & currFlag.getValue()) == currFlag.getValue()) { retFlags.add(currFlag); } } NotesACLAccess access = new NotesACLAccess(accessLevel, roles, retFlags); return access; } finally { Mem32.OSUnlockObject(hNamesList); } } } /** * Convenience method that call {@link #getEntries()} and returns a single * value for the specified name * * @param name name * @return acl entry or null if not found */ public NotesACLEntry getEntry(String name) { LinkedHashMap entries = getEntries(); NotesACLEntry entry = entries.get(name); if (entry==null) { for (Entry currEntry : entries.entrySet()) { if (NotesNamingUtils.equalNames(currEntry.getKey(), name)) { entry = currEntry.getValue(); break; } } } return entry; } /** * Returns all ACL entries * * @return ACL entries hashed by their username in the order they got returned from the C API */ public LinkedHashMap getEntries() { final Map rolesByIndex = getRolesByIndex(); final LinkedHashMap aclAccessInfoByName = new LinkedHashMap(); ACLENTRYENUMFUNC callback; if (PlatformUtils.isWin32()) { callback = new Win32NotesCallbacks.ACLENTRYENUMFUNCWin32() { @Override public void invoke(Pointer enumFuncParam, Pointer nameMem, short accessLevelShort, Pointer privileges, short accessFlag) { String name = NotesStringUtils.fromLMBCS(nameMem, -1); AclLevel accessLevel = AclLevel.toLevel((int) (accessLevelShort & 0xffff)); int iAccessFlag = (int) (accessFlag & 0xffff); EnumSet retFlags = EnumSet.noneOf(AclFlag.class); for (AclFlag currFlag : AclFlag.values()) { if ((iAccessFlag & currFlag.getValue()) == currFlag.getValue()) { retFlags.add(currFlag); } } byte[] privilegesArr = privileges.getByteArray(0, 10); List entryRoles = new ArrayList(); for (int i=5; i retFlags = EnumSet.noneOf(AclFlag.class); for (AclFlag currFlag : AclFlag.values()) { if ((iAccessFlag & currFlag.getValue()) == currFlag.getValue()) { retFlags.add(currFlag); } } byte[] privilegesArr = privileges.getByteArray(0, 10); List entryRoles = new ArrayList(); for (int i=5; i= NotesConstants.ACL_PRIVNAMEMAX) { throw new IllegalArgumentException("Role name length cannot (content within brackets) exceed "+(NotesConstants.ACL_PRIVNAMEMAX-1)+" characters"); } if (oldNameStripped.equals(newNameStripped)) { return; // nothing to do } String oldNameWithBrackets = "[" + oldNameStripped + "]"; String newNameWithBrackets = "[" + newNameStripped + "]"; Map rolesByIndex = getRolesByIndex(); int roleIndex = -1; int newRoleIndex = -1; for (Entry currEntry : rolesByIndex.entrySet()) { Integer currIndex = currEntry.getKey(); String currRole = currEntry.getValue(); if (currRole.equalsIgnoreCase(oldNameWithBrackets)) { roleIndex = currIndex.intValue(); } if (currRole.contentEquals(newNameWithBrackets)) { newRoleIndex = currIndex; } } if (roleIndex==-1) { throw new NotesError(0, "Role not found in ACL: "+oldName); } if (newRoleIndex!=-1) { throw new NotesError(0, "Role already exists in ACL: " + newName); } Memory newNameStrippedMem = NotesStringUtils.toLMBCS(newNameStripped, true); short result; if (PlatformUtils.is64Bit()) { result = NotesNativeAPI64.get().ACLSetPrivName(m_hACL64, (short) (roleIndex & 0xffff), newNameStrippedMem); } else { result = NotesNativeAPI32.get().ACLSetPrivName(m_hACL32, (short) (roleIndex & 0xffff), newNameStrippedMem); } NotesErrorUtils.checkResult(result); } /** * Returns all roles declared in the ACL * * @return roles */ public List getRoles() { List roles = new ArrayList(); short result; DisposableMemory retPrivName = new DisposableMemory(NotesConstants.ACL_PRIVSTRINGMAX); try { for (int i=5; i no more entries break; } NotesErrorUtils.checkResult(result); String role = NotesStringUtils.fromLMBCS(retPrivName, -1); if (!StringUtil.isEmpty(role)) { roles.add(role); } } else { result = NotesNativeAPI32.get().ACLGetPrivName(m_hACL32, (short) (i & 0xffff), retPrivName); if ((result & NotesConstants.ERR_MASK)==1060) { //Error "The name is not in the list" => no more entries break; } NotesErrorUtils.checkResult(result); String role = NotesStringUtils.fromLMBCS(retPrivName, -1); if (!StringUtil.isEmpty(role)) { roles.add(role); } } } } finally { retPrivName.dispose(); } return roles; } /** * Returns the role names hashed by their internal position * * @return roles */ private Map getRolesByIndex() { Map roles = new HashMap(); short result; Memory retPrivName = new Memory(NotesConstants.ACL_PRIVSTRINGMAX); for (int i=5; i no more entries break; } NotesErrorUtils.checkResult(result); String role = NotesStringUtils.fromLMBCS(retPrivName, -1); if (!StringUtil.isEmpty(role)) { roles.put(i, role); } } else { result = NotesNativeAPI32.get().ACLGetPrivName(m_hACL32, (short) (i & 0xffff), retPrivName); if ((result & NotesConstants.ERR_MASK)==1060) { //Error "The name is not in the list" => no more entries break; } NotesErrorUtils.checkResult(result); String role = NotesStringUtils.fromLMBCS(retPrivName, -1); if (!StringUtil.isEmpty(role)) { roles.put(i, role); } } } return roles; } /** * This function adds an entry to an access control list. * * @param name user or group to be added, either in abbreviated or canonical format * @param accessLevel Access level ({@link AclLevel}), of the entry to be added * @param roles roles to be set for this user * @param accessFlags Access level modifier flags ({@link AclFlag}), e.g.: unable to delete documents, unable to create documents, of the entry to be added */ public void addEntry(String name, AclLevel accessLevel, List roles, EnumSet accessFlags) { checkHandle(); List rolesFormatted; if (roles.isEmpty()) { rolesFormatted = roles; } else { boolean rolesOk = true; for (String currRole : roles) { if (!currRole.startsWith("[")) { rolesOk = false; break; } else if (!currRole.endsWith("]")) { rolesOk = false; break; } } if (rolesOk) { rolesFormatted = roles; } else { rolesFormatted = new ArrayList<>(); for (String currRole : roles) { if (!currRole.startsWith("[")) { currRole = "[" + currRole; } if (!currRole.endsWith("]")) { currRole = currRole + "]"; } rolesFormatted.add(currRole); } } } String nameCanonical = NotesNamingUtils.toCanonicalName(name); Map rolesByIndex = getRolesByIndex(); byte[] privilegesArr = new byte[NotesConstants.ACL_PRIVCOUNT / 8]; for (int i=5; i= NotesConstants.ACL_PRIVNAMEMAX) { throw new IllegalArgumentException("Role name length (content within brackets) cannot exceed "+(NotesConstants.ACL_PRIVNAMEMAX-1)+" characters"); } String roleWithBrackets = "[" + role + "]"; List roles = getRoles(); if (roles.contains(roleWithBrackets)) { return; } Map rolesByIndex = getRolesByIndex(); int freeIndex = -1; for (int i=5; i rolesByIndex = getRolesByIndex(); int roleIndex = -1; for (int i=5; i entries = getEntries(); for (Entry currEntry : entries.entrySet()) { String currName = currEntry.getKey(); NotesACLEntry currACLEntry = currEntry.getValue(); byte[] currPrivileges = currACLEntry.getPrivilegesArray(); if ((currPrivileges[byteOffsetWithBit] & bitToCheck) == bitToCheck) { byte[] newPrivileges = currPrivileges.clone(); newPrivileges[byteOffsetWithBit] = (byte) ((newPrivileges[byteOffsetWithBit] - bitToCheck & 0xff)); Memory currNameMem = NotesStringUtils.toLMBCS(currName, true); DisposableMemory newPrivilegesMem = new DisposableMemory(newPrivileges.length); newPrivilegesMem.write(0, newPrivileges, 0, newPrivileges.length); try { short result; if (PlatformUtils.is64Bit()) { result = NotesNativeAPI64.get().ACLUpdateEntry(m_hACL64, currNameMem, NotesConstants.ACL_UPDATE_PRIVILEGES, null, (short) 0, newPrivilegesMem, (short) 0); } else { result = NotesNativeAPI32.get().ACLUpdateEntry(m_hACL32, currNameMem, NotesConstants.ACL_UPDATE_PRIVILEGES, null, (short) 0, newPrivilegesMem, (short) 0); } NotesErrorUtils.checkResult(result); } finally { newPrivilegesMem.dispose(); } } } Memory emptyStrMem = NotesStringUtils.toLMBCS("", true); short result; if (PlatformUtils.is64Bit()) { result = NotesNativeAPI64.get().ACLSetPrivName(m_hACL64, (short) (roleIndex & 0xffff), emptyStrMem); } else { result = NotesNativeAPI32.get().ACLSetPrivName(m_hACL32, (short) (roleIndex & 0xffff), emptyStrMem); } NotesErrorUtils.checkResult(result); } /** * This function updates an entry in an access control list.
*
* Unless the user's name is specified to be modified, the information that is not specified to be * modified remains intact.
*
* If the user's name is specified to be modified, the user entry is deleted and a new entry is created.
* Unless the other access control information is specified to be modified as well, the other access control * information will be cleared and the user will have No Access to the database. * * @param name name of the entry to change * @param newName optional new entry name or null * @param newAccessLevel optional new entry access level or null * @param newRoles optional new entry roles or null * @param newFlags optional new acl flags or null */ public void updateEntry(String name, String newName, AclLevel newAccessLevel, List newRoles, EnumSet newFlags) { int updateFlags = 0; NotesACLEntry oldAclEntry = getEntry(name); if (oldAclEntry==null) { if (newName==null) { newName = name; } addEntry(newName, newAccessLevel, newRoles, newFlags); return; } Memory oldAclEntryNameMem = "-default-".equalsIgnoreCase(oldAclEntry.getName()) ? null : NotesStringUtils.toLMBCS(oldAclEntry.getName(), true); Memory newNameMem = null; if (newName!=null) { newName = NotesNamingUtils.toCanonicalName(newName); if (!NotesNamingUtils.equalNames(oldAclEntry.getName(), newName)) { updateFlags = updateFlags | NotesConstants.ACL_UPDATE_NAME; newNameMem = NotesStringUtils.toLMBCS(newName, true); } } int iNewAccessLevel = oldAclEntry.getAclLevel().getValue(); // TODO somehow it seems this flag always needs to be set // otherwise the level will be reset to NOACCESS, in case the level did not change updateFlags = updateFlags | NotesConstants.ACL_UPDATE_LEVEL; if (newAccessLevel!=null && !newAccessLevel.equals(oldAclEntry.getAclLevel())) { updateFlags = updateFlags | NotesConstants.ACL_UPDATE_LEVEL; iNewAccessLevel = newAccessLevel.getValue(); } DisposableMemory newPrivilegesMem = null; if (newRoles!=null && !newRoles.equals(oldAclEntry.getRoles())) { updateFlags = updateFlags | NotesConstants.ACL_UPDATE_PRIVILEGES; List newRolesFormatted; if (newRoles.isEmpty()) { newRolesFormatted = newRoles; } else { boolean rolesOk = true; for (String currRole : newRoles) { if (!currRole.startsWith("[")) { rolesOk = false; break; } else if (!currRole.endsWith("]")) { rolesOk = false; break; } } if (rolesOk) { newRolesFormatted = newRoles; } else { newRolesFormatted = new ArrayList<>(); for (String currRole : newRoles) { if (!currRole.startsWith("[")) { currRole = "[" + currRole; } if (!currRole.endsWith("]")) { currRole = currRole + "]"; } newRolesFormatted.add(currRole); } } } Map rolesByIndex = getRolesByIndex(); byte[] newPrivilegesArr = new byte[NotesConstants.ACL_PRIVCOUNT / 8]; for (int i=5; i m_accessFlags; private List m_roles; private NotesACLAccess(AclLevel accessLevel, List roles, EnumSet accessFlags) { m_accessLevel = accessLevel; m_roles = roles; m_accessFlags = accessFlags; } public List getRoles() { return m_roles; } public AclLevel getAclLevel() { return m_accessLevel; } public EnumSet getAclFlags() { return m_accessFlags; } public boolean isPerson() { return m_accessFlags.contains(AclFlag.PERSON); } public boolean isGroup() { return m_accessFlags.contains(AclFlag.GROUP); } public boolean isServer() { return m_accessFlags.contains(AclFlag.SERVER); } public boolean isAdminServer() { return m_accessFlags.contains(AclFlag.ADMIN_SERVER); } @Override public String toString() { return "NotesACLAccess [level="+m_accessLevel+", roles="+m_roles+", flags="+m_accessFlags+"]"; } } public static class NotesACLEntry extends NotesACLAccess { private String m_name; private byte[] m_privilegesArr; public NotesACLEntry(String name, AclLevel accessLevel, List roles, byte[] privilegesArr, EnumSet accessFlags) { super(accessLevel, roles, accessFlags); m_name = name; m_privilegesArr = privilegesArr; } public String getName() { return m_name; } byte[] getPrivilegesArray() { return m_privilegesArr; } @Override public String toString() { return "NotesACLEntry [name="+m_name+", level="+getAclLevel()+", roles="+getRoles()+", flags="+getAclFlags()+"]"; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy