View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.elasticsearch.index.objects.series;
23  
24  import org.opencastproject.elasticsearch.api.SearchMetadata;
25  import org.opencastproject.elasticsearch.impl.SearchMetadataCollection;
26  import org.opencastproject.security.api.AccessControlEntry;
27  import org.opencastproject.security.api.AccessControlList;
28  import org.opencastproject.security.api.AccessControlParser;
29  import org.opencastproject.security.api.Permissions;
30  import org.opencastproject.security.api.Permissions.Action;
31  import org.opencastproject.util.DateTimeSupport;
32  
33  import org.apache.commons.io.IOUtils;
34  import org.apache.commons.lang3.StringUtils;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  import java.io.IOException;
39  import java.nio.charset.Charset;
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.util.List;
43  import java.util.Map;
44  
45  import javax.xml.bind.Unmarshaller;
46  
47  /**
48   * Utility implementation to deal with the conversion of series and its corresponding index data structures.
49   */
50  public final class SeriesIndexUtils {
51  
52    private static final Logger logger = LoggerFactory.getLogger(SeriesIndexUtils.class);
53  
54    /**
55     * This is a utility class and should therefore not be instantiated.
56     */
57    private SeriesIndexUtils() {
58    }
59  
60    /**
61     * Creates a search result item based on the data returned from the search index.
62     *
63     * @param metadata
64     *          the search metadata
65     * @param unmarshaller the unmarshaller to use
66     * @return the search result item
67     * @throws IOException
68     *           if unmarshalling fails
69     */
70    public static Series toSeries(SearchMetadataCollection metadata, Unmarshaller unmarshaller) throws IOException {
71      Map<String, SearchMetadata<?>> metadataMap = metadata.toMap();
72      String seriesXml = (String) metadataMap.get(SeriesIndexSchema.OBJECT).getValue();
73      return Series.valueOf(IOUtils.toInputStream(seriesXml, Charset.defaultCharset()), unmarshaller);
74    }
75  
76    /**
77     * Creates search metadata from a series such that the event can be stored in the search index.
78     *
79     * @param series
80     *          the series
81     * @return the set of metadata
82     */
83    public static SearchMetadataCollection toSearchMetadata(Series series) {
84      SearchMetadataCollection metadata = new SearchMetadataCollection(
85              series.getIdentifier().concat(series.getOrganization()), Series.DOCUMENT_TYPE);
86      metadata.addField(SeriesIndexSchema.UID, series.getIdentifier(), false);
87      metadata.addField(SeriesIndexSchema.ORGANIZATION, series.getOrganization(), false);
88      metadata.addField(SeriesIndexSchema.OBJECT, series.toXML(), false);
89      metadata.addField(SeriesIndexSchema.TITLE, series.getTitle(), true);
90      if (StringUtils.trimToNull(series.getDescription()) != null) {
91        metadata.addField(SeriesIndexSchema.DESCRIPTION, series.getDescription(), true);
92      }
93      if (StringUtils.trimToNull(series.getSubject()) != null) {
94        metadata.addField(SeriesIndexSchema.SUBJECT, series.getSubject(), true);
95      }
96      if (StringUtils.trimToNull(series.getLanguage()) != null) {
97        metadata.addField(SeriesIndexSchema.LANGUAGE, series.getLanguage(), false);
98      }
99      if (StringUtils.trimToNull(series.getCreator()) != null) {
100       metadata.addField(SeriesIndexSchema.CREATOR, series.getCreator(), true);
101     }
102     if (StringUtils.trimToNull(series.getLicense()) != null) {
103       metadata.addField(SeriesIndexSchema.LICENSE, series.getLicense(), false);
104     }
105     if (StringUtils.trimToNull(series.getManagedAcl()) != null) {
106       metadata.addField(SeriesIndexSchema.MANAGED_ACL, series.getManagedAcl(), false);
107     }
108     if (series.getCreatedDateTime() != null) {
109       metadata.addField(SeriesIndexSchema.CREATED_DATE_TIME,
110               DateTimeSupport.toUTC(series.getCreatedDateTime().getTime()), false);
111     }
112     if (series.getOrganizers() != null) {
113       metadata.addField(SeriesIndexSchema.ORGANIZERS, series.getOrganizers().toArray(), true);
114     }
115     if (series.getContributors() != null) {
116       metadata.addField(SeriesIndexSchema.CONTRIBUTORS, series.getContributors().toArray(), true);
117     }
118     if (series.getPublishers() != null) {
119       metadata.addField(SeriesIndexSchema.PUBLISHERS, series.getPublishers().toArray(), true);
120     }
121     if (series.getRightsHolder() != null) {
122       metadata.addField(SeriesIndexSchema.RIGHTS_HOLDER, series.getRightsHolder(), true);
123     }
124 
125     if (!series.getExtendedMetadata().isEmpty()) {
126       addExtendedMetadata(metadata, series.getExtendedMetadata());
127     }
128 
129     if (StringUtils.trimToNull(series.getAccessPolicy()) != null) {
130       metadata.addField(SeriesIndexSchema.ACCESS_POLICY, series.getAccessPolicy(), false);
131       addAuthorization(metadata, series.getAccessPolicy());
132     }
133     if (series.getTheme() != null) {
134       metadata.addField(SeriesIndexSchema.THEME, series.getTheme(), false);
135     }
136     return metadata;
137   }
138 
139   /**
140    * Adds extended metadata fields to the input document
141    *
142    * @param doc
143    *          the input document
144    * @param extendedMetadata
145    *          the extended metadata map
146    */
147   private static void addExtendedMetadata(SearchMetadataCollection doc, Map<String, Map<String,
148           List<String>>> extendedMetadata) {
149     for (String type: extendedMetadata.keySet()) {
150       Map<String, List<String>> extendedMetadataByType = extendedMetadata.get(type);
151       for (String name: extendedMetadataByType.keySet()) {
152         List<String> values = extendedMetadataByType.get(name);
153         String fieldName = SeriesIndexSchema.EXTENDED_METADATA_PREFIX.concat(type + "_" + name);
154         doc.addField(fieldName, values, true);
155       }
156     }
157   }
158 
159   /**
160    * Adds authorization fields to the input document.
161    *
162    * @param doc
163    *          the input document
164    * @param aclString
165    *          the access control list string
166    */
167   private static void addAuthorization(SearchMetadataCollection doc, String aclString) {
168     Map<String, List<String>> permissions = new HashMap<>();
169 
170     // Define containers for common permissions
171     for (Action action : Permissions.Action.values()) {
172       permissions.put(action.toString(), new ArrayList<>());
173     }
174 
175     AccessControlList acl = AccessControlParser.parseAclSilent(aclString);
176     for (AccessControlEntry entry : acl.getEntries()) {
177       if (!entry.isAllow()) {
178         logger.info("Series index does not support denial via ACL, ignoring {}", entry);
179         continue;
180       }
181       List<String> actionPermissions = permissions.get(entry.getAction());
182       if (actionPermissions == null) {
183         actionPermissions = new ArrayList<>();
184         permissions.put(entry.getAction(), actionPermissions);
185       }
186       actionPermissions.add(entry.getRole());
187     }
188 
189     // Write the permissions to the input document
190     for (Map.Entry<String, List<String>> entry : permissions.entrySet()) {
191       String fieldName = SeriesIndexSchema.ACL_PERMISSION_PREFIX.concat(entry.getKey());
192       doc.addField(fieldName, entry.getValue(), false);
193     }
194   }
195 }