From 4e5340b267e136dd05de624151bee9f221213ac5 Mon Sep 17 00:00:00 2001 From: brinn <brinn> Date: Wed, 25 Jul 2012 09:25:53 +0000 Subject: [PATCH] [BIS-137] Implement a more secure ExperimentListPredicate. The new code checks all security-relevant aspects of the Experiment object, rather than just checking the experiment identifier. SVN: 26191 --- .../predicate/ExperimentListPredicate.java | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/server/authorization/predicate/ExperimentListPredicate.java diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/authorization/predicate/ExperimentListPredicate.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/authorization/predicate/ExperimentListPredicate.java new file mode 100644 index 00000000000..7a93432fdf2 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/authorization/predicate/ExperimentListPredicate.java @@ -0,0 +1,164 @@ +/* + * Copyright 2010 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.openbis.generic.server.authorization.predicate; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import net.lemnik.eodsql.BaseQuery; +import net.lemnik.eodsql.QueryTool; +import net.lemnik.eodsql.Select; + +import ch.systemsx.cisd.common.exceptions.AuthorizationFailureException; +import ch.systemsx.cisd.common.exceptions.Status; +import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.LongArrayMapper; +import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.StringArrayMapper; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment; +import ch.systemsx.cisd.openbis.generic.shared.authorization.RoleWithIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.ShouldFlattenCollections; +import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.AbstractSpacePredicate; +import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SpaceIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.util.SpaceCodeHelper; + +/** + * A predicate for lists of entities of {@link Experiment}s. + * <p> + * <i>This is an internal class. Do not use it as a user of the API.</i> + * + * @author Bernd Rinn + */ +@ShouldFlattenCollections(value = false) +public class ExperimentListPredicate extends AbstractSpacePredicate<List<Experiment>> +{ + // + // AbstractPredicate + // + + @Override + public String getCandidateDescription() + { + return "experiment"; + } + + @Override + protected Status doEvaluation(PersonPE person, List<RoleWithIdentifier> allowedRoles, + List<Experiment> experiments) + { + // All fields relevant for authorization are expected to be filled: + // - technical id + // - permanent id + // - identifier + final List<Long> ids = new ArrayList<Long>(experiments.size()); + final List<String> permIds = new ArrayList<String>(experiments.size()); + for (Experiment experiment : experiments) + { + if (experiment.getId() == null) + { + throw new AuthorizationFailureException("id is undefined."); + } + ids.add(experiment.getId()); + if (experiment.getPermId() == null) + { + throw new AuthorizationFailureException("permId is undefined."); + } + permIds.add(experiment.getPermId()); + + final SpaceIdentifier spaceIdentifier = + new ExperimentIdentifierFactory(experiment.getIdentifier()).createIdentifier(); + final String spaceCode = SpaceCodeHelper.getSpaceCode(person, spaceIdentifier); + final DatabaseInstancePE databaseInstance = getDatabaseInstance(spaceIdentifier); + final Status status = + evaluate(person, allowedRoles, databaseInstance, spaceCode); + if (Status.OK.equals(status) == false) + { + return status; + } + } + for (Long spaceId : getExperimentSpaceIds(ids, permIds)) + { + final Status status = evaluate(person, allowedRoles, spaceId); + if (Status.OK.equals(status) == false) + { + return status; + } + } + return Status.OK; + } + + private final static int ARRAY_SIZE_LIMIT = 999; + + interface IExperimentToSpaceQuery extends BaseQuery + { + @Select(sql = "select distinct space_id from projects p left join experiments e on e.proj_id = p.id " + + "where id = any(?{1}) union " + + "select distinct space_id from projects p left join experiments e on e.proj_id = p.id " + + "where perm_id = any(?{2})", parameterBindings = + { LongArrayMapper.class, StringArrayMapper.class }) + public List<Long> getExperimentSpaceIds(long[] experimentIds, String[] experimentPermIds); + } + + private Collection<Long> getExperimentSpaceIds(final List<Long> ids, final List<String> permIds) + { + if (ids.size() != permIds.size()) + { + throw new IllegalArgumentException("Expect to get the same number of ids and permIds."); + } + final int size = ids.size(); + if (size == 0) + { + return Collections.emptyList(); + } + final IExperimentToSpaceQuery query = + QueryTool.getQuery(authorizationDataProvider.getConnection(), + IExperimentToSpaceQuery.class); + if (size > ARRAY_SIZE_LIMIT) + { + final Set<Long> spaceIds = new HashSet<Long>(size); + for (int startIdx = 0; startIdx < size; startIdx += ARRAY_SIZE_LIMIT) + { + final List<Long> idSubList = ids.subList(startIdx, + Math.min(size, startIdx + ARRAY_SIZE_LIMIT)); + final List<String> permIdSubList = permIds.subList(startIdx, + Math.min(size, startIdx + ARRAY_SIZE_LIMIT)); + spaceIds.addAll(query.getExperimentSpaceIds(toArray(idSubList), + permIdSubList.toArray(new String[permIdSubList.size()]))); + } + return spaceIds; + } else + { + return query.getExperimentSpaceIds(toArray(ids), permIds.toArray(new String[size])); + } + } + + private long[] toArray(List<Long> list) + { + final long[] result = new long[list.size()]; + for (int i = 0; i < result.length; ++i) + { + result[i] = list.get(i); + } + return result; + } + +} -- GitLab