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.util;
23  
24  import static org.apache.commons.lang3.StringUtils.isBlank;
25  import static org.opencastproject.security.api.SecurityConstants.EPISODE_ROLE_ID_PREFIX;
26  import static org.opencastproject.security.api.SecurityConstants.GLOBAL_ADMIN_ROLE;
27  import static org.opencastproject.security.api.SecurityConstants.GLOBAL_ANONYMOUS_USERNAME;
28  import static org.opencastproject.security.api.SecurityConstants.GLOBAL_CAPTURE_AGENT_ROLE;
29  import static org.opencastproject.util.data.Tuple.tuple;
30  
31  import org.opencastproject.security.api.JaxbOrganization;
32  import org.opencastproject.security.api.JaxbRole;
33  import org.opencastproject.security.api.JaxbUser;
34  import org.opencastproject.security.api.Organization;
35  import org.opencastproject.security.api.OrganizationDirectoryService;
36  import org.opencastproject.security.api.SecurityConstants;
37  import org.opencastproject.security.api.SecurityService;
38  import org.opencastproject.security.api.UnauthorizedException;
39  import org.opencastproject.security.api.User;
40  import org.opencastproject.security.api.UserDirectoryService;
41  import org.opencastproject.util.ConfigurationException;
42  import org.opencastproject.util.NotFoundException;
43  import org.opencastproject.util.data.Tuple;
44  
45  import org.apache.commons.lang3.StringUtils;
46  import org.osgi.service.component.ComponentContext;
47  
48  import java.net.URL;
49  import java.util.Optional;
50  import java.util.regex.Pattern;
51  
52  /** Opencast security helpers. */
53  public final class SecurityUtil {
54    private static final Pattern SANITIZING_PATTERN = Pattern.compile("[^a-zA-Z0-9_]");
55  
56    private SecurityUtil() {
57    }
58  
59    /** The name of the key used to store the name of the system user in the global config. */
60    public static final String PROPERTY_KEY_SYS_USER = "org.opencastproject.security.digest.user";
61  
62    /**
63     * Run function <code>f</code> in the context described by the given organization and user.
64     *
65     * @param sec
66     *          Security service to use for getting data
67     * @param org
68     *          Organization to switch to
69     * @param user
70     *          User to switch to
71     * @param fn
72     *          Function to execute
73     */
74    public static void runAs(SecurityService sec, Organization org, User user, Runnable fn) {
75      final Organization prevOrg = sec.getOrganization();
76      final User prevUser = prevOrg != null ? sec.getUser() : null;
77      sec.setOrganization(org);
78      sec.setUser(user);
79      try {
80        fn.run();
81      } finally {
82        sec.setOrganization(prevOrg);
83        sec.setUser(prevUser);
84      }
85    }
86  
87    /**
88     * Create a system user for the given organization with global and organization local admin role. Get the
89     * <code>systemUserName</code> from the global config where it is stored under {@link #PROPERTY_KEY_SYS_USER}. In an
90     * OSGi environment this is typically done calling
91     * <code>componentContext.getBundleContext().getProperty(PROPERTY_KEY_SYS_USER)</code>.
92     *
93     * @see #createSystemUser(org.osgi.service.component.ComponentContext, org.opencastproject.security.api.Organization)
94     */
95    public static User createSystemUser(String systemUserName, Organization org) {
96      JaxbOrganization jaxbOrganization = JaxbOrganization.fromOrganization(org);
97      return new JaxbUser(systemUserName, null, jaxbOrganization, new JaxbRole(GLOBAL_ADMIN_ROLE, jaxbOrganization),
98              new JaxbRole(org.getAdminRole(), jaxbOrganization));
99    }
100 
101   /**
102    * Create the global anonymous user with the given organization.
103    *
104    * @param org
105    *          the organization
106    * @return the global anonymous user
107    */
108   public static User createAnonymousUser(Organization org) {
109     JaxbOrganization jaxbOrganization = JaxbOrganization.fromOrganization(org);
110     return new JaxbUser(GLOBAL_ANONYMOUS_USERNAME, null, jaxbOrganization, new JaxbRole(
111             jaxbOrganization.getAnonymousRole(), jaxbOrganization));
112   }
113 
114   /**
115    * Create a system user for the given organization with global admin role. The system user name is fetched from the
116    * global OSGi config.
117    *
118    * @see #createSystemUser(String, org.opencastproject.security.api.Organization)
119    */
120   public static User createSystemUser(ComponentContext cc, Organization org) {
121     final String systemUserName = cc.getBundleContext().getProperty(PROPERTY_KEY_SYS_USER);
122     return createSystemUser(systemUserName, org);
123   }
124 
125   /**
126    * Fetch the system user name from the configuration.
127    *
128    * @see #PROPERTY_KEY_SYS_USER
129    */
130   public static String getSystemUserName(ComponentContext cc) {
131     final String systemUserName = cc.getBundleContext().getProperty(PROPERTY_KEY_SYS_USER);
132     if (systemUserName != null) {
133       return systemUserName;
134     } else {
135       throw new ConfigurationException(
136               "An Opencast installation always needs a system user name. Please configure one under the key "
137                       + PROPERTY_KEY_SYS_USER);
138     }
139   }
140 
141   /**
142    * Get a user and an organization. Only returns something if both elements can be determined.
143    */
144   public static Optional<Tuple<User, Organization>> getUserAndOrganization(SecurityService sec,
145           OrganizationDirectoryService orgDir, String orgId, UserDirectoryService userDir, String userId) {
146     final Organization prevOrg = sec.getOrganization();
147     try {
148       final Organization org = orgDir.getOrganization(orgId);
149       sec.setOrganization(org);
150       return Optional.ofNullable(userDir.loadUser(userId))
151               .map(user -> tuple(user, org));
152     } catch (NotFoundException e) {
153       return Optional.empty();
154     } finally {
155       sec.setOrganization(prevOrg);
156     }
157   }
158 
159   /** Extract hostname and port number from a URL. */
160   public static Tuple<String, Integer> hostAndPort(URL url) {
161     int port = url.getPort();
162     if (port < 0) {
163       port = url.getDefaultPort();
164     }
165     return tuple(StringUtils.strip(url.getHost(), "/"), port);
166   }
167 
168   /**
169    * Check if the current user has access to the capture agent with the given id.
170    * @param agentId
171    *           The agent id to check.
172    * @throws UnauthorizedException
173    *           If the user doesn't have access.
174    */
175   public static void checkAgentAccess(final SecurityService securityService, final String agentId)
176       throws UnauthorizedException {
177     if (isBlank(agentId)) {
178       return;
179     }
180     final User user = securityService.getUser();
181     if (user.hasRole(SecurityConstants.GLOBAL_ADMIN_ROLE) || user.hasRole(user.getOrganization().getAdminRole())) {
182       return;
183     }
184     if (!user.hasRole(SecurityUtil.getCaptureAgentRole(agentId))) {
185       throw new UnauthorizedException(user, "schedule");
186     }
187   }
188 
189   private static String sanitizeCaName(final String ca) {
190     return SANITIZING_PATTERN.matcher(ca).replaceAll("").toUpperCase();
191   }
192 
193   /**
194    * Get the role name of the role required to access the capture agent with the given agent id.
195    *
196    * @param
197    *     agentId The id of the agent to get the role for.
198    * @return
199    *     The role name.
200    */
201   public static String getCaptureAgentRole(final String agentId) {
202     return GLOBAL_CAPTURE_AGENT_ROLE + "_" + sanitizeCaName(agentId);
203   }
204 
205   /**
206    * Get the episode role id for a mediapackage and an action
207    *
208    * @param mediaPackageId the id of the mediapackage
209    * @param action the action as a string
210    * @return the role
211    */
212   public static String getEpisodeRoleId(final String mediaPackageId, final String action) {
213     return EPISODE_ROLE_ID_PREFIX + "_" + mediaPackageId + "_" + action.toUpperCase();
214   }
215 }