Hi
This can be done with creating an extension of a Classloader that implements the loading of the third party plugin and the security constrains you like to set for the plugins is implemented in an extension of an SecurityManager.
I created such and plugin based system for a game (that of course never was completed). In the game, events could occure in with random time intervall. Some events was that the missile you attack someone with hit Santa instead and the all children didn’t get their christmas presents. Events like these where created by extending a know interface. And I thought that other people would like to help create such events so I created the plugin based system.
As security constratins I choosed the following:
- All implemented events must lie in the package org.backmask.nuclearwar.randomevents.impl
- Events can only read files under the directory the game is installed
- Events can only write to files under the directory randomevents that lies directly under the directory the game is installed.
Some extra demands I set
- All events plugin must be zipped into a file called .nwjar. The file shall contain all graphics/sounds and classes needed to exceute the event.
- The nwjar file must have a meta-inf/Manifest.mf file specifing what class implements the event Interface.
And here is the code. If you have question about anything ask.
SecurityManager
package org.backmask.nuclearwar.security;
import java.io.File;
import java.io.FilePermission;
import java.security.Permission;
import java.util.PropertyPermission;
/**
* @author P950MBC
*
* To change this generated comment edit the template variable "typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class NuclearWarSecurityManager extends SecurityManager {
public void checkPermission(Permission perm){
checkPermission(perm, null);
}
public void checkPermission(Permission perm, Object context){
//if the permission orginates from a untrusted randomevent, grant permission if not
String className = isRandomEvents();
if (className == null)
return;
String action = perm.getActions();
String name = perm.getName();
//A untrusted randomevents is trying to access the file system
if(perm instanceof FilePermission){
//Make sure that the file that are being accessed is under the user.dir
String pwd = (new File(System.getProperty("user.dir"))).getAbsolutePath();
String abspath = (new File(name)).getAbsolutePath();
if(!(abspath.startsWith(pwd)/* || abspath.toUpperCase().startsWith(javahome.toUpperCase())*/))
fail(perm, context);
//Untrusted randomevents can read any file in the Nuclear War folder
if(action.equals("read"))
return;
//Untrusted randomevents don´t have write access anywhere but the randomevents folder
//and only to the events zip file(event.zip) and the events propertyfile(event.property).
if(action.equals("write")){
//Check if the file is in the randomevents subfolder
pwd += File.separator + "randomevents";
if (!abspath.startsWith(pwd))
fail(perm, context);
String fileName = (new File(name)).getName();
String ext = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
fileName = fileName.substring(0, fileName.lastIndexOf("."));
className = className.substring(className.lastIndexOf(".") + 1, className.length());
//Controlls that the file has the right name and the right extensions
if (fileName.equals(className) && (ext.equals("zip") || ext.equals("property")))
return;
fail(perm, context);
}
}
if(perm instanceof PropertyPermission){
if(action.equals("read"))
return;
if(action.equals("write"))
fail(perm, context);
}
System.out.println("<<<< Failing permission " + perm);
fail(perm, context);
}
private String isRandomEvents(){
Class classes[] = getClassContext();
for (int i = 0; i < classes.length; i++){
if (classes[i].getName().startsWith("org.backmask.nuclearwar.randomevents.impl.")){
return classes[i].getName();
}
}
return null;
}
private final void fail(Permission perm, Object context){
throw new SecurityException(makeMessage(perm, context));
}
private final String makeMessage(Permission perm, Object context){
String classname = perm.getClass().getName();
String action = perm.getActions();
String name = perm.getName();
StringBuffer sb = new StringBuffer(80);
sb.append(classname).append('.').append(action);
sb.append('(').append(name).append("), context=").append(context);
Class classctx[] = getClassContext();
for(int i = 0; i < classctx.length; i++){
String ctxname = classctx[i].getName();
sb.append("\n ").append(ctxname);
}
return sb.toString();
}
}