view src/nabble/view/lib/Permissions.java @ 21:aba8ed4c8a06

semiprivate
author Franklin Schmidt <fschmidt@gmail.com>
date Sat, 13 Jun 2020 22:30:48 -0600
parents 18cf4872fd7f
children
line wrap: on
line source

package nabble.view.lib;

import fschmidt.db.Listener;
import fschmidt.db.ListenerList;
import fschmidt.util.java.Filter;
import nabble.model.Db;
import nabble.model.Init;
import nabble.model.Node;
import nabble.model.Person;
import nabble.model.Site;
import nabble.model.User;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;


public final class Permissions {
	private Permissions() {}  // never

	private static String encode(String s) {
		StringBuilder buf = new StringBuilder();
		int n = s.length();
		for( int i=0; i<n; i++ ) {
			char c = s.charAt(i);
			if( c == '\'' )
				buf.append('\'');
			buf.append(c);
		}
		return buf.toString();
	}

	private static void dbCheck(Site site) {
		if( !site.getDb().isInTransaction() )
			throw new RuntimeException("not in transaction");
	}

	public static boolean isInGroup(User user,String group) {
		return user.getSite().hasTags(null,user,
			"label='group:" + encode(group) + "'"
		);
	}

	public static void addToGroup(User user,String group) {
		Site site = user.getSite();
		dbCheck(site);
		site.addTag( null, user, "group:" + group );
		groupChangeListeners.event(site);
	}

	public static void removeFromGroup(User user,String group) {
		Site site = user.getSite();
		dbCheck(site);
		user.getSite().deleteTags(null,user,
			"label='group:" + encode(group) + "'"
		);
		groupChangeListeners.event(site);
	}

	public static void removeGroup(Site site,String group) {
		site.deleteTags(
			"node_id is null and user_id is not null and label='group:" + encode(group) + "'"
		);
		groupChangeListeners.event(site);
	}

	public static void removeGroups(User user) {
		user.getSite().deleteTags(null,user,
			"label like 'group:%'"
		);
		groupChangeListeners.event(user.getSite());
	}

	private static List<String> getGroups(Site site,String sqlCondition) {
		List<String> list = new ArrayList<String>();
		for( String label : site.findTagLabels(sqlCondition) ) {
			list.add( label.substring(6) );
		}
		return list;
	}

	public static List<String> getGroups(User user) {
		return getGroups( user.getSite(),
			"node_id is null and user_id=" + user.getId() + " and label like 'group:%'"
		);
	}

	public static List<String> getGroups(Site site) {
		return getGroups( site,
			"node_id is null and user_id is not null and label like 'group:%'"
		);
	}

	public static List<User> getUsersInGroup(Site site,String group) {
		return site.findTagUsers(
			"node_id is null and user_id is not null and label='group:" + encode(group) + "'"
		);
	}

	public static void addPermission(Node node,String permission) {
		Site site = node.getSite();
		dbCheck(site);
		site.addTag( node, null, "permission:" + permission );
		permissionChangeListeners.event(site);
	}

	public static void addPermission(Node node,String permission,String group) {
		Site site = node.getSite();
		dbCheck(site);
		if( !nodeHasPermission(node,permission) )
			addPermission(node,permission);
		site.addTag( node, null, "permission:" + permission + ":" + group );
		permissionChangeListeners.event(site);
	}

	public static void addPermission(Site site,String permission,String group) {
		dbCheck(site);
		if( !siteHasPermission(site,permission) )
			site.addTag( null, null, "permission:" + permission );
		site.addTag( null, null, "permission:" + permission + ":" + group );
		permissionChangeListeners.event(site);
	}

	public static void removePermission(Node node,String permission,String group) {
		Site site = node.getSite();
		dbCheck(site);
		site.deleteTags(node,null,
			"label='permission:" + encode(permission) + ":" + encode(group) + "'"
		);
		permissionChangeListeners.event(site);
	}

	public static void removePermission(Node node,String permission) {
		Site site = node.getSite();
		site.deleteTags(node,null,
			"(label = 'permission:" + encode(permission) + "'"
				+ " or label like 'permission:" + encode(permission) + ":%')"
		);
		permissionChangeListeners.event(site);
	}

	public static void removePermissions(Node node) {
		Site site = node.getSite();
		dbCheck(site);
		site.deleteTags(node,null,
			"label like 'permission:%'"
		);
		permissionChangeListeners.event(site);
	}

	public static boolean isPermissionVersion(Site site,String version) {
		return site.hasTags(null,null,
			"label='permission-version:" + version + "'"
		);
	}

	public static void setPermissionVersion(Site site,String version) {
		dbCheck(site);
		deletePermissionVersion(site);
		site.addTag( null, null, "permission-version:" + version );
	}

	public static void deletePermissionVersion(Site site) {
		site.deleteTags(null,null,
			"(label like 'permission:%' or label like 'site_default_permission:%' or label like 'permission-version:%')"
		);
	}

	private static String query(Node node) {
		return node==null ? "node_id is null" : "node_id=" + node.getId();
	}

	private static boolean siteHasPermission(Site site,String permission) {
		return site.hasTags(null,null,
			"label='permission:" + encode(permission) + "'"
		);
	}

	public static boolean nodeHasPermission(Node node,String permission) {
		return node.getSite().hasTags(node,null,
			"label='permission:" + encode(permission) + "'"
		);
	}

	public static Node getPermissionNode(Node node,String permission) {
		for( Node n : node.getAncestors() ) {
			if( nodeHasPermission(n,permission) )
				return n;
		}
		return null;
	}

	public static List<String> getGroupsWithPermission(Node node,String permission) {
		Site site = node.getSite();
		node = getPermissionNode(node,permission);
		if( node == null && !siteHasPermission(site,permission) )
			return Collections.emptyList();
		List<String> list = new ArrayList<String>();
		String labelStart = "permission:" + encode(permission) + ":";
		int i = labelStart.length();
		for( String label : site.findTagLabels(
			query(node) + " and user_id is null and label like '" + labelStart + "%'"
		) ) {
			list.add( label.substring(i) );
		}
		return list;
	}

	public static boolean hasGroupsWithPermission(Node node,String permission) {
		Site site = node.getSite();
		node = getPermissionNode(node,permission);
		if( node == null && !siteHasPermission(site,permission) )
			return false;
		return site.hasTags(node,null,
			"label like 'permission:" + encode(permission) + ":%'"
		);
	}

	public static final String ANYONE_GROUP = "Anyone";
	public static final String AUTHOR_GROUP = "Authors";
	public static final String ADMINISTRATORS_GROUP = "Administrators";

	public static List<String> getPersonGroups(User user) {
		if( user==null || !user.isRegistered() )
			return new ArrayList<String>();
		List<String> groups = getGroups(user);
		groups.add(ANYONE_GROUP);
		return groups;
	}

	public static boolean hasPermission(Node permissionNode,Node targetNode,User user,String permission) {
		Person owner = targetNode.getOwner();
		Site site = permissionNode.getSite();
		permissionNode = getPermissionNode(permissionNode,permission);
		if( permissionNode == null && !siteHasPermission(site,permission) )
			return false;
		String s = "label='permission:" + encode(permission) + ":";
		List<String> groups = getPersonGroups(user);
		if( owner.equals(user) )
			groups.add(AUTHOR_GROUP);
		for( String group : groups ) {
			if( site.hasTags(permissionNode,null, s + encode(group) + "'" ) ) {
				return true;
			}
		}
		return false;
	}

	public static boolean hasPermission(Node node,String group,String permission) {
		Site site = node.getSite();
		node = getPermissionNode(node,permission);
		if( node == null && !siteHasPermission(site,permission) )
			return false;
		return hasPermission(site,node,group,permission);
	}

	private static boolean hasPermission(Site site,Node node,String group,String permission) {
		return site.hasTags(node,null,
			"label='permission:" + encode(permission) + ":" + encode(group) + "'"
		);
	}

	public static boolean hasPermission(Site site,String group,String permission) {
		return siteHasPermission(site,permission) && site.hasTags(null,null,
			"label='permission:" + encode(permission) + ":" + encode(group) + "'"
		);
	}

	public static List<User> getUsersWithPermission(Node node,String permission) {
		Site site = node.getSite();
		node = getPermissionNode(node,permission);
		if( node == null && !siteHasPermission(site,permission) )
			return Collections.emptyList();
		if (hasPermission(site, node, ANYONE_GROUP,permission))
			return site.getUsers("registered is not null");
		String labelStart = "permission:" + encode(permission) + ":";
		int i = labelStart.length();
		return site.findTagUsers(
			"node_id is null and user_id is not null and label in ("
			+	"select 'group:' || substring(label," + (i+1) + ") from tag where " + query(node) + " and user_id is null and label like '" + labelStart + "%'"
			+")"
		);
	}


	public static final String VIEW_PERMISSION = "View";

	public static boolean isPrivate(Node node) {
		return getPrivateNode(node) != null;
	}

	public static boolean canBeViewedByParentViewers(Node node) {
		if( node.getKind() != Node.Kind.APP )
			return true;
		if( !nodeHasPermission(node,VIEW_PERMISSION) )
			return true;
		Node parent = node.getParent();
		if( parent != null )
			parent = getPrivateNode(parent);
		if( parent == null && !siteHasPermission(node.getSite(),VIEW_PERMISSION) )
			return !isPrivate(node);
		return !node.getSite().hasTags(parent,null,
			"label like 'permission:View:%'"
			+" and label not in (select label from tag where node_id=" + node.getId() + " and user_id is null and label like 'permission:View:%')"
		);
	}

	public static final Filter<Node> canBeViewedByParentViewersFilter = new Filter<Node>() {
		public boolean ok(Node node) {
			return canBeViewedByParentViewers(node);
		}
	};

	public static boolean canBeViewedByPerson(Node node,User user) {
		if( user != null ) {
			if( isSysAdmin(user) )
				return true;
			if( isInGroup(user,ADMINISTRATORS_GROUP) )
				return true;
		}
		if( node.getSite().getRootNode().getOwner().equals(user) )
			return true;
		Node permissionNode = node.getApp();
		if( permissionNode==null )
			permissionNode = node.getSite().getRootNode();
		return hasPermission(permissionNode,node,user,VIEW_PERMISSION);
	}

	public static final Filter<Node> canBeViewedByPersonFilter(final User user) {
		return new Filter<Node>() {
			public boolean ok(Node node) {
				return canBeViewedByPerson(node,user);
			}
		};
	}

	public static Node getPrivateNode(Node node) {
		node = getPermissionNode(node,VIEW_PERMISSION);
		return node==null || hasPermission(node.getSite(),node,ANYONE_GROUP,VIEW_PERMISSION) ? null : node;
	}

	public static Node getPrivateNodeForSearch(Node node) {
		node = getPrivateNode(node);
		while( node != null && canBeViewedByParentViewers(node) ) {
			node = getPrivateNode(node.getParent());
		}
		return node;
	}

	// Banning ------------------------------------------------------

	public static boolean isBanned(User user) {
		return user.getSite().hasTags(null,user,"label='banned'");
	}

	public static void ban(User user) {
		user.getSite().addTag(null, user, "banned");
	}

	public static void unban(User user) {
		user.getSite().deleteTags(null,user,"label='banned'");
	}

	public static List<User> getBannedUsers(Site site) {
		return site.findTagUsers(
			"node_id is null and user_id is not null and label='banned'"
		);
	}



	private static final Set<String> sysadmins = Init.get("sysadmins",Collections.<String>emptySet());

	public static boolean isSysAdmin(User user) {
		return sysadmins.contains(user.getEmail());
	}


	// site permissions

	public static void addSitePermission(Site site,String permission) {
		dbCheck(site);
		site.addTag( null, null, "site_permission:" + permission );
	}

	public static void addSiteDefaultPermission(Site site,String permission) {
		dbCheck(site);
		site.addTag( null, null, "site_default_permission:" + permission );
	}

	private static boolean hasSitePermission(Site site,String permission) {
		return site.hasTags(null,null,
			"label='site_permission:" + encode(permission) + "'"
		);
	}

	public static boolean siteHasSitePermission(Site site,String permission) {
		return hasSitePermission(site,permission);
	}

	private static boolean hasSiteDefaultPermission(Site site,String permission) {
		return site.hasTags(null,null,
			"label='site_default_permission:" + encode(permission) + "'"
		);
	}

	public static void addSitePermission(Site site,String permission,String group) {
		dbCheck(site);
		if( !hasSitePermission(site,permission) )
			site.addTag( null, null, "site_permission:" + permission );
		site.addTag( null, null, "site_permission:" + permission + ":" + group );
	}

	public static void addSiteDefaultPermission(Site site,String permission,String group) {
		dbCheck(site);
		if( !hasSiteDefaultPermission(site,permission) )
			site.addTag( null, null, "site_default_permission:" + permission );
		site.addTag( null, null, "site_default_permission:" + permission + ":" + group );
	}

	public static void removeSitePermission(Site site,String permission,String group) {
		dbCheck(site);
		site.deleteTags(null,null,
			"label='site_permission:" + encode(permission) + ":" + encode(group) + "'"
		);
	}

	public static void removeSitePermission(Site site,String permission) {
		site.deleteTags(null,null,
			"(label = 'site_permission:" + encode(permission) + "'"
				+ " or label like 'site_permission:" + encode(permission) + ":%')"
		);
	}

	public static void removeSitePermissions(Site site) {
		dbCheck(site);
		site.deleteTags(null,null,
			"label like 'site_permission:%'"
		);
	}

	private static String sitePermissionLabel(Site site,String permission) {
		if( hasSitePermission(site,permission) )
			return "site_permission:" + encode(permission);
		else if( hasSiteDefaultPermission(site,permission) )
			return "site_default_permission:" + encode(permission);
		else
			return null;
	}

	public static List<String> getGroupsWithSitePermission(Site site,String permission) {
		String labelStart = sitePermissionLabel(site,permission);
		if( labelStart == null )
			return Collections.emptyList();
		List<String> list = new ArrayList<String>();
		int i = labelStart.length();
		for( String label : site.findTagLabels(
			"node_id is null and user_id is null and label like '" + labelStart + ":%'"
		) ) {
			list.add( label.substring(i) );
		}
		return list;
	}

	public static boolean hasGroupsWithSitePermission(Site site,String permission) {
		String labelStart = sitePermissionLabel(site,permission);
		if( labelStart == null )
			return false;
		return site.hasTags(null,null,
			"label like '" + labelStart + ":%'"
		);
	}

	public static boolean hasSitePermission(Site site,User user,String permission) {
		String labelStart = sitePermissionLabel(site,permission);
		if( labelStart == null )
			return false;
		String s = "label='" + labelStart + ":";
		List<String> groups = getPersonGroups(user);
		for( String group : groups ) {
			if( site.hasTags(null,null, s + encode(group) + "'" ) )
				return true;
		}
		return false;
	}

	public static boolean hasSitePermission(Site site,String group,String permission) {
		String labelStart = sitePermissionLabel(site,permission);
		if( labelStart == null )
			return false;
		return site.hasTags(null,null,
			"label='" + labelStart + ":" + encode(group) + "'"
		);
	}

	public static boolean hasSiteDefaultPermission(Site site,String group,String permission) {
		return hasSiteDefaultPermission(site,permission) && site.hasTags(null,null,
			"label='site_default_permission:" + encode(permission) + ":" + encode(group) + "'"
		);
	}

	private static final ListenerList<Site> groupChangeListeners = new ListenerList<Site>();

	public static void addGroupChangeListener(final Listener<Site> listener) {
		groupChangeListeners.add(listener);
	}

	private static final ListenerList<Site> permissionChangeListeners = new ListenerList<Site>();

	public static void addPermissionChangeListener(final Listener<Site> listener) {
		permissionChangeListeners.add(listener);
	}

}