Skip to content

Instantly share code, notes, and snippets.

@PEMapModder
Created December 20, 2014 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PEMapModder/02aab229783a39e273d1 to your computer and use it in GitHub Desktop.
Save PEMapModder/02aab229783a39e273d1 to your computer and use it in GitHub Desktop.
How to implement plugin permissions (for all servers written in OOP languages with PocketMine-style API: commands, events, schedules) in code for access to API functions

How to implement plugin permisisons (for all servers written in OOP languages with PocketMine-style API: commands, events, schedules) in code for access to API functions

The principle of this system is to make use of the characteristic of executing code on one thread - code flows single-direction - to identify what context (server or what plugin or what) the code is flowing to currently and decide whether the current context has permission to do something.

First, the server has to declare a class that extends a class called Context and instantiate an object that has any permissions. The object serves as a lock and shall not be accessible by plugins. Every object that represents a plugin (but these objects must not be declared by the plugin) should have a context too. This is an example for the Context class in Java:

import java.util.List;

public class Context{
	private List<UsedPermission> perms;
	private List<Context> owningContexts;
	public boolean usesPermission(UsedPermission perm){
		for(UsedPermission hasPerm : perms){
			if(hasPerm == perm){
				return true;
			}
		}
		return false;
	}
	public boolean ownsContext(Context other){
		if(other == this){
			return true;
		}
		for(Context ctx: owningContexts){
			if(ctx == other){
				return true;
			}
		}
		return false;
	}
}

Then, in the main server class, there should be a private static property runningContext of type Context and another called mainThread of type Thread (or thread ID). In the constructor of the server object, runningContext should be set to the context lock and mainThread should be set to represent the current thread.

In the manifest (e.g. plugin.yml) for every plugin, a used-permissions property should be set to declare the permissions that the plugin uses. If a plugin interacts with another, it must specify it in the manifest too.

Every time the server calls to an interface/abstract method of the plugin (like onEnable() or onCommand() in PocketMine. that runs arbitrary code specified by a plugin), it should call this method on the server object: (in Java for example)

public void switchCurrentContext(Context newContext, Context lock) throws IllegalAccessException{
	if(Thread.getCurrentThread() != this.mainThread){
		throw new IllegalAccessException("This method must be accessed from the main thread"); // this avoids access from other threads started by the plugin that knocks the door when the context is back to the server lock
	}
	if(lock != this.lock){
		throw new IllegalAccessException("Illegal lock");
	}
	this.currentContext = newContext;
}

Such that when a plugin calls an API function, the API function checks this:

public void iAmAnApiFunction(Context ctx) throws IllegalAccessException{
	UsedPermission permission_to_use_this-function;
	if(!server.isContextOwnedByCurentContext(ctx)){
		throw new IllegalAccessException("Illegal context passed");
	}
	if(!ctx.usePermission(permission_to_use_this_function)){
		throw new IlelgalAccessException("Context does not use permission to use this function");
	}
}

Improvements

A java.util.Random object can be used if the

Bugs of this system

The system is vulnerable to reflection attacks. For example, in PocketMine PHP, this is possible:

/** @var \pocketmine\Server $server */
$property = new \ReflectionClass($server)->getProperty("context"); // assuming that the private context lock is stored in $server->context
$property->setAccessible(true);
$context = $property->getValue($server); // Yay! We seized the throne!

A fix to this method is not to pass any objects that directly or indirectly have reference to the lock to plugins and to keep them away from the global context and static properties. However, in some cases this may carelessly lead to garbaging of the lock.

Another fix is to review it by man that a plugin doesn't include such malicious code, although in this case the system is as useful as non-existent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment