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.userdirectory.studip;
23  
24  import org.opencastproject.security.api.Organization;
25  import org.opencastproject.security.api.OrganizationDirectoryService;
26  import org.opencastproject.security.api.RoleProvider;
27  import org.opencastproject.security.api.UserProvider;
28  import org.opencastproject.util.NotFoundException;
29  
30  import org.apache.commons.lang3.StringUtils;
31  import org.osgi.framework.BundleContext;
32  import org.osgi.framework.ServiceRegistration;
33  import org.osgi.service.cm.ConfigurationException;
34  import org.osgi.service.cm.ManagedServiceFactory;
35  import org.osgi.service.component.ComponentContext;
36  import org.osgi.service.component.annotations.Activate;
37  import org.osgi.service.component.annotations.Component;
38  import org.osgi.service.component.annotations.Reference;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  import java.lang.management.ManagementFactory;
43  import java.net.URI;
44  import java.net.URISyntaxException;
45  import java.util.Dictionary;
46  import java.util.Map;
47  import java.util.concurrent.ConcurrentHashMap;
48  
49  import javax.management.MalformedObjectNameException;
50  import javax.management.ObjectName;
51  
52  /**
53   * Studip implementation of the spring UserDetailsService, taking configuration information from the component context.
54   */
55  @Component(
56      immediate = true,
57      service = ManagedServiceFactory.class,
58      property = {
59          "service.pid=org.opencastproject.userdirectory.studip",
60          "service.description=Provides Studip user directory instances"
61      }
62  )
63  public class StudipUserProviderFactory implements ManagedServiceFactory {
64  
65    /** The logger */
66    protected static final Logger logger = LoggerFactory.getLogger(StudipUserProviderFactory.class);
67  
68    /** This service factory's PID */
69    public static final String PID = "org.opencastproject.userdirectory.studip";
70  
71    /** The key to look up the organization identifer in the service configuration properties */
72    private static final String ORGANIZATION_KEY = "org.opencastproject.userdirectory.studip.org";
73  
74    /** The key to look up the URL of the Studip instance */
75    private static final String STUDIP_URL_KEY = "org.opencastproject.userdirectory.studip.url";
76  
77    /** The key to look up the user token to use for performing searches. */
78    private static final String STUDIP_TOKEN_KEY = "org.opencastproject.userdirectory.studip.token";
79  
80    /** The key to look up the number of user records to cache */
81    private static final String CACHE_SIZE = "org.opencastproject.userdirectory.studip.cache.size";
82  
83    /** The key to look up the number of minutes to cache users */
84    private static final String CACHE_EXPIRATION = "org.opencastproject.userdirectory.studip.cache.expiration";
85  
86    /** A map of pid to studip user provider instance */
87    private Map<String, ServiceRegistration> providerRegistrations = new ConcurrentHashMap<String, ServiceRegistration>();
88  
89    /** The OSGI bundle context */
90    protected BundleContext bundleContext = null;
91  
92    /** The organization directory service */
93    private OrganizationDirectoryService orgDirectory;
94  
95    /** OSGi callback for setting the organization directory service. */
96    @Reference
97    public void setOrgDirectory(OrganizationDirectoryService orgDirectory) {
98      this.orgDirectory = orgDirectory;
99    }
100 
101   /**
102    * Callback for the activation of this component
103    *
104    * @param cc
105    *          the component context
106    */
107   @Activate
108   public void activate(ComponentContext cc) {
109     logger.debug("Activate StudipUserProviderFactory");
110     this.bundleContext = cc.getBundleContext();
111   }
112 
113   /**
114    * {@inheritDoc}
115    *
116    * @see org.osgi.service.cm.ManagedServiceFactory#getName()
117    */
118   @Override
119   public String getName() {
120     return PID;
121   }
122 
123   /**
124    * {@inheritDoc}
125    *
126    * @see org.osgi.service.cm.ManagedServiceFactory#updated(java.lang.String, java.util.Dictionary)
127    */
128   @Override
129   public void updated(String pid, Dictionary properties) throws ConfigurationException {
130 
131     logger.debug("updated StudipUserProviderFactory");
132 
133     String organization = (String) properties.get(ORGANIZATION_KEY);
134     if (StringUtils.isBlank(organization)) {
135       throw new ConfigurationException(ORGANIZATION_KEY, "is not set");
136     }
137 
138     URI url = null;
139     try {
140       url = new URI((String) properties.get(STUDIP_URL_KEY));
141     } catch (URISyntaxException e) {
142       throw new ConfigurationException(STUDIP_URL_KEY, "URI is invalid", e);
143     }
144     if (StringUtils.isBlank(url.toString())) {
145       throw new ConfigurationException(STUDIP_URL_KEY, "is not set");
146     }
147 
148     String token = (String) properties.get(STUDIP_TOKEN_KEY);
149     if (StringUtils.isBlank(token)) {
150       throw new ConfigurationException(STUDIP_TOKEN_KEY, "is not set");
151     }
152 
153     int cacheSize = 1000;
154     try {
155       if (properties.get(CACHE_SIZE) != null) {
156         Integer configuredCacheSize = Integer.parseInt(properties.get(CACHE_SIZE).toString());
157         if (configuredCacheSize != null) {
158           cacheSize = configuredCacheSize.intValue();
159         }
160       }
161     } catch (Exception e) {
162       throw new ConfigurationException("{} could not be loaded", CACHE_SIZE);
163     }
164 
165 
166     int cacheExpiration = 60;
167     try {
168       if (properties.get(CACHE_EXPIRATION) != null) {
169         Integer configuredCacheExpiration = Integer.parseInt(properties.get(CACHE_EXPIRATION).toString());
170         if (configuredCacheExpiration != null) {
171           cacheExpiration = configuredCacheExpiration.intValue();
172         }
173       }
174     } catch (Exception e) {
175       throw new ConfigurationException("{} could not be loaded", CACHE_EXPIRATION);
176     }
177 
178     // Now that we have everything we need, go ahead and activate a new provider, removing an old one if necessary
179     ServiceRegistration existingRegistration = providerRegistrations.remove(pid);
180     if (existingRegistration != null) {
181       existingRegistration.unregister();
182     }
183 
184     Organization org;
185     try {
186       org = orgDirectory.getOrganization(organization);
187     } catch (NotFoundException e) {
188       logger.warn("Organization {} not found!", organization);
189       throw new ConfigurationException(ORGANIZATION_KEY, "not found");
190     }
191 
192     logger.debug("creating new StudipUserProviderInstance for pid=" + pid);
193     StudipUserProviderInstance provider = new StudipUserProviderInstance(pid,
194             org, url, token, cacheSize, cacheExpiration);
195 
196     providerRegistrations.put(pid, bundleContext.registerService(UserProvider.class.getName(), provider, null));
197     providerRegistrations.put(pid, bundleContext.registerService(RoleProvider.class.getName(), provider, null));
198 
199   }
200 
201   /**
202    * {@inheritDoc}
203    *
204    * @see org.osgi.service.cm.ManagedServiceFactory#deleted(java.lang.String)
205    */
206   @Override
207   public void deleted(String pid) {
208     logger.debug("delete StudipUserProviderInstance for pid=" + pid);
209     ServiceRegistration registration = providerRegistrations.remove(pid);
210     if (registration != null) {
211       registration.unregister();
212       try {
213         ManagementFactory.getPlatformMBeanServer().unregisterMBean(StudipUserProviderFactory.getObjectName(pid));
214       } catch (Exception e) {
215         logger.warn("Unable to unregister mbean for pid='{}': {}", pid, e.getMessage());
216       }
217     }
218   }
219 
220   /**
221    * Builds a JMX object name for a given PID
222    *
223    * @param pid
224    *          the PID
225    * @return the object name
226    * @throws NullPointerException
227    * @throws MalformedObjectNameException
228    */
229   public static final ObjectName getObjectName(String pid) throws MalformedObjectNameException, NullPointerException {
230     return new ObjectName(pid + ":type=StudipRequests");
231   }
232 
233 }