/*
*
* Copyright 2014 McEvoy Software Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.milton.http.annotated;
import io.milton.annotations.AccessControlList;
import io.milton.annotations.Post;
import io.milton.http.Auth;
import io.milton.http.HttpManager;
import io.milton.http.Request;
import io.milton.http.Request.Method;
import io.milton.resource.AccessControlledResource;
import io.milton.resource.AccessControlledResource.Priviledge;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Attempt to locate an Access Control List of the given resource for the
* current user
*
* @author brad
*/
public class AccessControlListAnnotationHandler extends AbstractAnnotationHandler {
private static final Logger log = LoggerFactory.getLogger(AccessControlListAnnotationHandler.class);
public AccessControlListAnnotationHandler(final AnnotationResourceFactory outer) {
super(outer, AccessControlList.class);
}
/**
* Get priviledges for the current user
*
* @param curUser
* @param res
* @param auth
* @return
*/
public Set<AccessControlledResource.Priviledge> availablePrivs(Object curUser, AnnoResource res, Auth auth) {
Set<Priviledge> privs = directPrivs(curUser, res, auth);
if (privs != null) {
return privs;
}
AnnoCollectionResource p = res.getParent();
while (p != null) {
privs = directPrivs(curUser, p, auth);
if (privs != null) {
return privs;
}
p = p.getParent();
}
// BM: this shouldnt be here. We do want to grant all privs at this point, because nothing
// explicit was found, but only if there is a current user. And privs should use Priviledge.ALL
// rather then allOf(..) because we want to use the more concise, unexpanded, priviledge set
//privs = EnumSet.allOf(Priviledge.class);
if (curUser != null) {
log.info("No explicit AccessControl annotation found, so defaulting to full access for logged in user");
privs = new HashSet<Priviledge>();
privs.add(Priviledge.ALL);
}
return privs;
}
public Set<AccessControlledResource.Priviledge> directPrivs(Object curUser, AnnoResource res, Auth auth) {
Set<AccessControlledResource.Priviledge> acl = EnumSet.noneOf(AccessControlledResource.Priviledge.class);
Object source = res.getSource();
List<ControllerMethod> availMethods = getMethods(source.getClass());
if (availMethods.isEmpty()) {
log.warn("No ACL methods were found");
return null;
} else {
try {
for (ControllerMethod cm : availMethods) {
addPrivsFromMethod(cm.method, cm.controller, acl, curUser, res, auth);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return acl;
}
private void addPrivsFromMethod(java.lang.reflect.Method method, Object target, Set<AccessControlledResource.Priviledge> acl, Object curUser, AnnoResource res, Auth auth) throws Exception {
Object currentUserSource = null;
if (curUser != null) {
if (curUser instanceof AnnoResource) {
AnnoResource ar = (AnnoResource) curUser;
currentUserSource = ar.getSource();
} else {
currentUserSource = curUser;
}
}
// Check that currentUserSource is compatible with the 2nd arg of the method
if (method.getParameterTypes().length < 2) {
return;
}
if (currentUserSource != null) {
Class<?> pt = method.getParameterTypes()[1];
if (!pt.isAssignableFrom(currentUserSource.getClass())) {
log.info("ACL method second arg {} is not assignable from current user type {}", pt, currentUserSource.getClass());
return;
}
}
Object[] args = annoResourceFactory.buildInvokeArgsExt(res, currentUserSource, true, method, curUser, res, auth);
Object result = method.invoke(target, args);
if (result == null) {
// ignore
} else if (result instanceof Collection) {
Collection col = (Collection) result;
for (Object o : col) {
if (o instanceof Priviledge) {
Priviledge p = (Priviledge) o;
acl.add(p);
}
}
} else {
if (result instanceof Priviledge) {
Priviledge p = (Priviledge) result;
acl.add(p);
}
}
}
public Priviledge requiredPriv(AnnoResource res, Method httpMethod, Request request) {
if (httpMethod.equals(Method.POST)) {
Priviledge p = getRequiredPostPriviledge(request, res);
if (p == null) {
p = Priviledge.READ_CONTENT;
}
return p;
} else if (httpMethod == Method.ACL) {
return Priviledge.READ_ACL;
} else if (httpMethod == Method.UNLOCK) {
return Priviledge.UNLOCK;
} else if (httpMethod == Method.PROPFIND) {
return Priviledge.READ_PROPERTIES;
} else if (httpMethod.isWrite) {
return Priviledge.WRITE_CONTENT;
} else {
return Priviledge.READ_CONTENT;
}
}
private Priviledge getRequiredPostPriviledge(Request request, AnnoResource res) {
ControllerMethod cm = annoResourceFactory.postAnnotationHandler.getPostMethod(res, request, HttpManager.request().getParams());
if (cm == null) {
return null;
} else {
Post p = cm.method.getAnnotation(Post.class);
if (p != null) {
return p.requiredPriviledge();
} else {
return null;
}
}
}
}