People usually agree that security is a separate concern,
it shouldn't be mixed with business logic. There are
many way to achieve this goal in java. The most common
solution is to use
Proxy pattern (protection proxy).
Basically, a protection proxy is an object that implements the same
interface that the business model does.
It also maitains a reference to the business model and
delegates function calls to it, but before performing the function
it does the security check. It may be a bit confusing,
so let's see some code.
The interface that both the business model (object thhat will be protected)
and the protection proxy implement.
public interface ISecret { String read(); void write(String message); }
The business model (protected object):
public class Secret implements ISecret { private String message = ""; public String read() { return message; } public void write(String message) { this.message = message; } }
The protection proxy:
public class SecuredSecret implements ISecret { private ISecret secret; private IToken token; public SecuredSecret(IToken token, ISecret secret) { this.secret = secret; this.token = token; } public String read() { if(!token.hasPermission("Secret","read")) throw new RuntimeException(); return secret.read(); } public void write(String message) { if(!token.hasPermission("Secret","write")) throw new RuntimeException(); secret.write(message); } }
Let's try it:
//contruct an IDomain and log in IToken token = domain.login(LOGIN_A,PASSWORD_A); ISecret realSecret = new Secret(); ISecret securedSecret = new SecuredSecret(token, realSecret); try { // The user need permission [ Secret, write ] to call succesfully this function securedSecret.write("very important"); } catch(RuntimeException e) { e.printStackTrace(); } token.invalidate();
Java has the ability to create such proxy objects "on the fly" with the
help of java.lang.reflect.InvocationHandler.
Ori is shipped with a simple implementation:
ori.impl.utils.proxy.SecurityProxyFactory.
After using it's secure function on a business model,
each function (declared in the interface) will be protected.
The permission's module name will be the buisness model's simple name
(in this example: Secret) and it's action name will be the method's
name (in this exemple: read, write).
Note that a ori.impl.utils.proxy.SecurityProxyException is
thrown if the permission has no right to perform the action!
Let's see some code:
IToken token = domain.login(LOGIN_A,PASSWORD_A); ISecret realSecret = new Secret(); ISecret securedSecret = (ISecret) SecurityProxyFactory .secure(token,realSecret,ISecret.class); try { // The user need permission [ Secret, write ] to call succesfully this function securedSecret.write("very important"); } catch(SecurityProxyException e) { e.printStackTrace(); } token.invalidate();
This implementation has some issues (overloaded functions and classes with similar simple name may have to share their permissions), it was created to show the power of dynamic proxies. The creation of a more sophisticated version is up to you.