blob: ce4203cff26b8b64f6a4e66b014f59d9fbf6f57b [file] [log] [blame]
/*
* Copyright 2019 The Apache Software Foundation.
*
* 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 org.apache.juddi.security.rbac;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import javax.xml.ws.WebServiceContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.juddi.api_v3.AccessLevel;
import org.apache.juddi.api_v3.Action;
import org.apache.juddi.api_v3.EntityType;
import org.apache.juddi.api_v3.GetPermissionsMessageRequest;
import org.apache.juddi.api_v3.GetPermissionsMessageResponse;
import org.apache.juddi.api_v3.Permission;
import org.apache.juddi.api_v3.SetPermissionsMessageRequest;
import org.apache.juddi.api_v3.SetPermissionsMessageResponse;
import org.apache.juddi.config.PersistenceManager;
import org.apache.juddi.config.ResourceConfig;
import org.apache.juddi.model.UddiEntity;
import org.apache.juddi.model.UddiEntityPublisher;
import org.apache.juddi.security.IAccessControl;
import org.uddi.api_v3.BindingTemplate;
import org.uddi.api_v3.BusinessEntity;
import org.uddi.api_v3.BusinessInfo;
import org.uddi.api_v3.BusinessService;
import org.uddi.api_v3.Name;
import org.uddi.api_v3.OperationalInfo;
import org.uddi.api_v3.PublisherAssertion;
import org.uddi.api_v3.RelatedBusinessInfo;
import org.uddi.api_v3.RelatedBusinessInfos;
import org.uddi.api_v3.ServiceInfo;
import org.uddi.api_v3.ServiceInfos;
import org.uddi.api_v3.SharedRelationships;
import org.uddi.api_v3.TModel;
import org.uddi.api_v3.TModelInfo;
import org.uddi.api_v3.TModelInfos;
import org.uddi.v3_service.DispositionReportFaultMessage;
/**
* RBAC functionality, to be pulled from J2EE container, such as Tomcat or
* Jboss, for obtaining user roles.<br><br>
* Permissions are then calculated based on an inheritance model, similar to
* most systems. I.e.<br><br>
* If a permission doesn't exist for a binding, the service's permission set
* then applies. If the service's permission set is not defined, then the
* business's permission set applies.
* <br><br>
* If the requestor does not have permission for the entity, all content is
* nulled out and replaced with a 'redacted' string. This will preserve
* functionality for pagination operations, limits, offsets, etc.
*
* @author Alex O'Ree
* @since 3.4
*/
public class RoleBasedAccessControlImpl implements IAccessControl {
private static final Log log = LogFactory.getLog(RoleBasedAccessControlImpl.class);
private static final String REDACTED = ResourceConfig.getGlobalMessage("rbac.redacted");
public static final String EVERYONE = "everyone";
private void redact(BusinessService bs) {
bs.setBusinessKey(REDACTED);
bs.setServiceKey(REDACTED);
bs.setBindingTemplates(null);
bs.setCategoryBag(null);
bs.getDescription().clear();
bs.getSignature().clear();
bs.getName().clear();
bs.getName().add(new Name(REDACTED, "en"));
}
private boolean hasReadAccess(WebServiceContext ctx, List<RbacRulesModel> rules, String username) {
for (RbacRulesModel r : rules) {
if (r.getContainerRole().equalsIgnoreCase(EVERYONE)) {
if (r.getAccessLevelAsEnum() == AccessLevel.NONE) //explicit deny
{
return false;
}
}
if (ctx.isUserInRole(r.getContainerRole())) {
if (r.getAccessLevelAsEnum() == AccessLevel.NONE) //explicit deny
{
return false;
}
return true;
}
if (ctx.getUserPrincipal() != null && ctx.getUserPrincipal().getName().equals(username)) {
if (r.getAccessLevelAsEnum() == AccessLevel.NONE) //explicit deny
{
return false;
}
return true;
}
}
return false;
}
private boolean has(WebServiceContext ctx, List<RbacRulesModel> rules, AccessLevel requiredLevel) {
for (RbacRulesModel r : rules) {
if (r.getContainerRole().equalsIgnoreCase(EVERYONE)) {
if (r.getAccessLevelAsEnum().getLevel() >= requiredLevel.getLevel()) {
return true;
}
}
if (ctx.isUserInRole(r.getContainerRole())) {
if (r.getAccessLevelAsEnum().getLevel() >= requiredLevel.getLevel()) {
return true;
}
}
}
return false;
}
private List<RbacRulesModel> getPermissionSet(String serviceKey) {
EntityManager em = PersistenceManager.getEntityManager();
EntityTransaction tx = em.getTransaction();
List<RbacRulesModel> set = new ArrayList<>();
try {
tx.begin();
Query createQuery = em.createQuery("select c from RbacRulesModel c where c.uddiEntityId=:id");
createQuery.setParameter("id", serviceKey);
set = createQuery.getResultList();
} finally {
if (tx.isActive()) {
tx.rollback();
}
em.close();
}
return set;
}
private UddiEntity loadEntity(String serviceKey, Class clazz) {
EntityManager em = PersistenceManager.getEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
try {
return (UddiEntity) em.find(clazz, serviceKey);
} catch (ClassCastException e) {
}
} finally {
if (tx.isActive()) {
tx.rollback();
}
em.close();
}
return null;
}
private void redact(BindingTemplate bt) {
bt.setBindingKey(REDACTED);
bt.getDescription().clear();
bt.getSignature().clear();
bt.setHostingRedirector(null);
bt.setAccessPoint(null);
bt.setTModelInstanceDetails(null);
bt.setAccessPoint(null);
bt.setCategoryBag(null);
}
@Override
public List<BusinessService> filterServices(WebServiceContext ctx, UddiEntityPublisher username, List<BusinessService> items) {
//load access rules from database
for (BusinessService bs : items) {
//get the permission for this entity.
UddiEntity ue = loadEntity(bs.getServiceKey(), org.apache.juddi.model.BusinessService.class);
if (ue == null) {
redact(bs);
continue; //access denied
}
if (username == null) {
redact(bs);
continue; //access denied
}
if (username.isOwner(ue)) {
//keep it
continue;
}
List<RbacRulesModel> rules = getPermissionSet(bs.getServiceKey());
if (rules.isEmpty()) {
//get the rules for the parent business
rules = getPermissionSet(bs.getBusinessKey());
}
if (rules.isEmpty()) {
redact(bs);
continue; //access denied
}
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
redact(bs); //also access denied, either no matching role or an explicit deny
continue;
}
filterBindingTemplates(ctx, username, bs.getBindingTemplates().getBindingTemplate());
}
return new ArrayList(items);
}
@Override
public List<BusinessEntity> filterBusinesses(WebServiceContext ctx, UddiEntityPublisher username, List<BusinessEntity> items) {
//load access rules from database
for (BusinessEntity bs : items) {
//get the permission for this entity.
UddiEntity ue = loadEntity(bs.getBusinessKey(), org.apache.juddi.model.BusinessEntity.class);
if (ue == null) {
redact(bs);
continue; //access denied
}
if (username == null) {
redact(bs);
continue; //access denied
}
if (username.isOwner(ue)) {
//keep it
continue;
}
List<RbacRulesModel> rules = getPermissionSet(bs.getBusinessKey());
if (rules.isEmpty()) {
redact(bs);
continue; //access denied
}
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
redact(bs); //also access denied, either no matching role or an explicit deny
continue;
}
filterServices(ctx, username, bs.getBusinessServices().getBusinessService());
}
return new ArrayList(items);
}
@Override
public List<BusinessInfo> filterBusinessInfo(WebServiceContext ctx, UddiEntityPublisher username, List<BusinessInfo> items) {
//load access rules from database
for (BusinessInfo bs : items) {
//get the permission for this entity.
UddiEntity ue = loadEntity(bs.getBusinessKey(), org.apache.juddi.model.BusinessService.class);
if (ue == null) {
redact(bs);
continue; //access denied
}
if (username == null) {
redact(bs);
continue; //access denied
}
if (username.isOwner(ue)) {
//keep it
continue;
}
List<RbacRulesModel> rules = getPermissionSet(bs.getBusinessKey());
if (rules.isEmpty()) {
redact(bs);
continue; //access denied
}
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
redact(bs); //also access denied, either no matching role or an explicit deny
continue;
}
if (bs.getServiceInfos() != null) {
filterServiceInfo(ctx, username, bs.getServiceInfos());
}
}
return new ArrayList(items);
}
@Override
public List<TModel> filterTModels(WebServiceContext ctx, UddiEntityPublisher username, List<TModel> items) {
//load access rules from database
for (TModel bs : items) {
//get the permission for this entity.
UddiEntity ue = loadEntity(bs.getTModelKey(), org.apache.juddi.model.Tmodel.class);
if (ue == null) {
redact(bs);
continue; //access denied
}
if (username == null) {
redact(bs);
continue; //access denied
}
if (username.isOwner(ue)) {
//keep it
continue;
}
List<RbacRulesModel> rules = getPermissionSet(bs.getTModelKey());
if (rules.isEmpty()) {
redact(bs);
continue; //access denied
}
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
redact(bs); //also access denied, either no matching role or an explicit deny
continue;
}
}
return new ArrayList(items);
}
@Override
public List<BindingTemplate> filterBindingTemplates(WebServiceContext ctx, UddiEntityPublisher username, List<BindingTemplate> items) {
//load access rules from database
for (BindingTemplate bs : items) {
//get the permission for this entity.
UddiEntity ue = loadEntity(bs.getBindingKey(), org.apache.juddi.model.BindingTemplate.class);
if (ue == null) {
redact(bs);
continue; //access denied
}
if (username == null) {
redact(bs);
continue; //access denied
}
if (username.isOwner(ue)) {
//keep it
continue;
}
List<RbacRulesModel> rules = getPermissionSet(bs.getBindingKey());
if (rules.isEmpty()) {
rules = getPermissionSet(bs.getServiceKey());
}
if (rules.isEmpty()) {
redact(bs);
continue; //access denied
}
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
redact(bs); //also access denied, either no matching role or an explicit deny
}
}
return new ArrayList(items);
}
@Override
public RelatedBusinessInfos filtedRelatedBusinessInfos(WebServiceContext ctx, UddiEntityPublisher username, RelatedBusinessInfos items) {
//TODO
if (items == null) {
return null;
}
for (RelatedBusinessInfo bs : items.getRelatedBusinessInfo()) {
UddiEntity ue = loadEntity(bs.getBusinessKey(), org.apache.juddi.model.BusinessService.class);
if (ue == null) {
redact(bs);
continue; //access denied
}
if (username == null) {
redact(bs);
continue; //access denied
}
if (username.isOwner(ue)) {
//keep it
continue;
}
List<RbacRulesModel> rules = getPermissionSet(bs.getBusinessKey());
if (rules.isEmpty()) {
redact(bs);
continue; //access denied
}
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
redact(bs); //also access denied, either no matching role or an explicit deny
continue;
}
if (bs.getSharedRelationships() != null) {
for (SharedRelationships si : bs.getSharedRelationships()) {
boolean redact = false;
for (PublisherAssertion pa : si.getPublisherAssertion()) {
UddiEntity ue2 = loadEntity(pa.getFromKey(), org.apache.juddi.model.BusinessEntity.class);
if (ue2 == null) {
redact = true;
break;
}
if (username == null) {
redact = true;
break; //access denied
}
if (username.isOwner(ue)) {
//keep it
continue;
}
List<RbacRulesModel> rules2 = getPermissionSet(pa.getFromKey());
if (rules2.isEmpty()) {
redact = true;
break; //access denied
}
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
redact = true; //also access denied, either no matching role or an explicit deny
break;
}
ue2 = loadEntity(pa.getToKey(), org.apache.juddi.model.BusinessEntity.class);
if (ue2 == null) {
redact = true;
break;
}
if (username.isOwner(ue2)) {
//keep it
continue;
}
rules2 = getPermissionSet(pa.getToKey());
if (rules2.isEmpty()) {
redact = true;
break; //access denied
}
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
redact = true; //also access denied, either no matching role or an explicit deny
break;
}
}
if (redact) {
}
}
}
}
return items;
}
@Override
public ServiceInfos filterServiceInfo(WebServiceContext ctx, UddiEntityPublisher username, ServiceInfos items) {
if (items == null) {
return null;
}
for (ServiceInfo si : items.getServiceInfo()) {
UddiEntity ue = loadEntity(si.getServiceKey(), org.apache.juddi.model.BusinessService.class);
if (ue == null) {
si.setServiceKey(REDACTED);
continue; //access denied
}
if (username == null) {
si.setServiceKey(REDACTED);
continue; //access denied
}
if (username.isOwner(ue)) {
//keep it
continue;
}
List<RbacRulesModel> rules = getPermissionSet(si.getServiceKey());
if (!rules.isEmpty() && !hasReadAccess(ctx, rules, username.getAuthorizedName())) {
si.setServiceKey(REDACTED);
}
if (rules.isEmpty()) {
rules = getPermissionSet(si.getBusinessKey());
if (rules.isEmpty()) {
si.setBusinessKey(REDACTED);
} else {
if (!hasReadAccess(ctx, rules, username.getAuthorizedName())) {
si.setBusinessKey(REDACTED);
}
}
}
}
return items;
}
@Override
public TModelInfos filterTModelInfo(WebServiceContext ctx, UddiEntityPublisher username, TModelInfos items) {
//TODO
return (items);
}
@Override
public List<OperationalInfo> filterOperationalInfo(WebServiceContext ctx, UddiEntityPublisher username, List<OperationalInfo> items) {
//TODO
return new ArrayList(items);
}
private void redact(BusinessEntity bs) {
bs.setBusinessKey(REDACTED);
bs.setBusinessServices(null);
bs.setCategoryBag(null);
bs.setContacts(null);
bs.setDiscoveryURLs(null);
bs.setIdentifierBag(null);
bs.getDescription().clear();
bs.getSignature().clear();
bs.getName().clear();
bs.getName().add(new Name(REDACTED, "en"));
}
@Override
public GetPermissionsMessageResponse getPermissions(GetPermissionsMessageRequest arg0) throws DispositionReportFaultMessage, RemoteException {
EntityManager em = PersistenceManager.getEntityManager();
EntityTransaction tx = em.getTransaction();
List<RbacRulesModel> set = new ArrayList<>();
try {
tx.begin();
Query createQuery = null;
if (arg0.getEntityId() != null && arg0.getEntityId().length() > 0) {
createQuery = em.createQuery("select c from RbacRulesModel c where c.uddiEntityId=:id", RbacRulesModel.class);
createQuery.setParameter("id", arg0.getEntityId());
} else {
createQuery = em.createQuery("select c from RbacRulesModel c", RbacRulesModel.class);
}
set = createQuery.getResultList();
} finally {
if (tx.isActive()) {
tx.rollback();
}
em.close();
}
GetPermissionsMessageResponse response = new GetPermissionsMessageResponse();
for (RbacRulesModel item : set) {
Permission permission = new Permission();
permission.setEntityId(item.getUddiEntityId());
permission.setLevel((item.getAccessLevelAsEnum()));
permission.setAction(Action.NOOP);
permission.setTarget(item.getContainerRole());
//TODO permission.setType(item.);
response.getLevel().add(permission);
}
return response;
}
@Override
public SetPermissionsMessageResponse setPermissions(SetPermissionsMessageRequest arg0) throws DispositionReportFaultMessage, RemoteException {
EntityManager em = PersistenceManager.getEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
for (Permission perm : arg0.getLevel()) {
if (perm.getAction() != Action.NOOP) {
Query createQuery = null;
createQuery = em.createQuery("delete from RbacRulesModel e where e.uddiEntityId=:id and e.containerRole=:user");
createQuery.setParameter("id", perm.getEntityId());
createQuery.setParameter("user", perm.getTarget());
createQuery.executeUpdate();
}
if (perm.getAction() == Action.ADD) {
RbacRulesModel r = new RbacRulesModel();
r.setAccessLevel(perm.getLevel().name());
r.setContainerRole(perm.getTarget());
r.setUddiEntityId(perm.getEntityId());
r.setId(UUID.randomUUID().toString());
em.persist(r);
}
}
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
em.close();
}
SetPermissionsMessageResponse response = new SetPermissionsMessageResponse();
return response;
}
private void redact(BusinessInfo bs) {
bs.setBusinessKey(REDACTED);
bs.getDescription().clear();
bs.setServiceInfos(null);
bs.getName().clear();
bs.getName().add(new Name(REDACTED, "en"));
}
private void redact(TModel bs) {
bs.setTModelKey(REDACTED);
bs.getDescription().clear();
bs.setCategoryBag(null);
bs.setName(new Name(REDACTED, "en"));
bs.getDescription().clear();
bs.getOverviewDoc().clear();
bs.getSignature().clear();
bs.setIdentifierBag(null);
}
private void redact(RelatedBusinessInfo bs) {
bs.setBusinessKey(REDACTED);
bs.getDescription().clear();
bs.getName().clear();
bs.getName().add(new Name(REDACTED, "en"));
bs.getSharedRelationships().clear();
}
@Override
public boolean hasPermission(AccessLevel level, WebServiceContext ctx, UddiEntityPublisher actor, String entityid, EntityType type) {
UddiEntity ue = null;
switch (type) {
case BINDING:
ue = loadEntity(entityid, org.apache.juddi.model.BindingTemplate.class);
break;
case BUSINESS:
ue = loadEntity(entityid, org.apache.juddi.model.BusinessEntity.class);
break;
case SERVICE:
ue = loadEntity(entityid, org.apache.juddi.model.BusinessService.class);
break;
case TMODEL:
ue = loadEntity(entityid, org.apache.juddi.model.Tmodel.class);
break;
default:
log.warn("umhandled case for " + type);
}
if (ue == null) {
return false;
}
if (actor == null) {
return false;
}
if (actor.isOwner(ue)) {
return true;
}
List<RbacRulesModel> rules = getPermissionSet(entityid);
if (rules.isEmpty()) {
return false;
}
return has(ctx, rules, level);
}
}