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.security.api;
23  
24  import java.util.AbstractMap.SimpleEntry;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Objects;
31  
32  import javax.xml.bind.annotation.XmlAccessType;
33  import javax.xml.bind.annotation.XmlAccessorType;
34  import javax.xml.bind.annotation.XmlElement;
35  import javax.xml.bind.annotation.XmlRootElement;
36  import javax.xml.bind.annotation.XmlType;
37  
38  /**
39   * A list of {@link AccessControlEntry}s.
40   */
41  @XmlAccessorType(XmlAccessType.FIELD)
42  @XmlType(name = "acl", namespace = "http://org.opencastproject.security", propOrder = { "entries" })
43  @XmlRootElement(name = "acl", namespace = "http://org.opencastproject.security")
44  public final class AccessControlList {
45  
46    /** The list of access control entries */
47    @XmlElement(name = "ace")
48    private List<AccessControlEntry> entries;
49  
50    /**
51     * No-arg constructor needed by JAXB
52     */
53    public AccessControlList() {
54      this.entries = new ArrayList<>();
55    }
56  
57    public AccessControlList(AccessControlEntry... entries) {
58      this.entries = new ArrayList<>(Arrays.asList(entries));
59    }
60  
61    public AccessControlList(List<AccessControlEntry> entries) {
62      this.entries = new ArrayList<>(entries);
63    }
64  
65    /**
66     * @return the entries
67     */
68    public List<AccessControlEntry> getEntries() {
69      return entries;
70    }
71  
72    public boolean isValid() {
73      return entries.stream().allMatch(AccessControlEntry::isValid);
74    }
75  
76    /**
77     * {@inheritDoc}
78     *
79     * @see java.lang.Object#toString()
80     */
81    @Override
82    public String toString() {
83      return entries.toString();
84    }
85  
86    /**
87     * Merge this access control list with another one based on roles specified within. In case both lists specify
88     * rules for a specific role, the set of rules from the access control list passed as argument to this method will
89     * take precedence over the internal set of rules.
90     *
91     * Example:
92     * <pre>
93     *          ROLE_USER1   ROLE_USER2   ROLE_USER3
94     *          read  write  read  write  read  write
95     * this     ok    ok     ok    ok
96     * argument              ok           ok
97     * result   ok    ok     ok           ok
98     * </pre>
99     *
100    * @param acl
101    *        Access control list to merge with
102    * @return Merged access control list
103    */
104   public AccessControlList merge(AccessControlList acl) {
105     HashSet<String> roles = new HashSet<>();
106     ArrayList<AccessControlEntry> newEntries = new ArrayList<>(acl.getEntries());
107     // Get list of new roles
108     for (AccessControlEntry entry : newEntries) {
109       roles.add(entry.getRole());
110     }
111     // Apply old rules if no new rules for a role exist
112     for (AccessControlEntry entry : this.entries) {
113       if (!roles.contains(entry.getRole())) {
114         newEntries.add(entry);
115       }
116     }
117     this.entries = newEntries;
118     return this;
119   }
120 
121   /**
122    * Merge this access control list with another one based on actions specified within. In case both lists specify
123    * rules for a specific action, the rules from the access control list passed as argument to this method will take
124    * precedence over the internal rules.
125    *
126    * Example:
127    * <pre>
128    *          ROLE_USER1   ROLE_USER2   ROLE_USER3
129    *          read  write  read  write  read  write
130    * this     ok    ok     ok    ok
131    * argument              ok           ok
132    * result   ok    ok     ok    ok     ok
133    * </pre>
134    *
135    * @param acl
136    *        Access control list to merge with
137    * @return Merged access control list
138    */
139   public AccessControlList mergeActions(AccessControlList acl) {
140     HashMap<SimpleEntry<String, String>, AccessControlEntry> rules = new HashMap<>();
141     SimpleEntry<String, String> key;
142     for (AccessControlEntry entry : this.entries) {
143       key = new SimpleEntry<>(entry.getRole(), entry.getAction());
144       rules.put(key, entry);
145     }
146     for (AccessControlEntry entry : acl.getEntries()) {
147       key = new SimpleEntry<>(entry.getRole(), entry.getAction());
148       rules.put(key, entry);
149     }
150     this.entries = new ArrayList<>(rules.values());
151     return this;
152   }
153 
154   @Override
155   public boolean equals(Object o) {
156     if (o instanceof AccessControlList) {
157       return entries.equals(((AccessControlList) o).entries);
158     }
159     return false;
160   }
161 
162   @Override
163   public int hashCode() {
164     return Objects.hash(entries);
165   }
166 
167 }