1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.userdirectory.moodle;
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.SecurityConstants;
28 import org.opencastproject.security.api.UserProvider;
29 import org.opencastproject.util.NotFoundException;
30
31 import org.apache.commons.lang3.BooleanUtils;
32 import org.apache.commons.lang3.StringUtils;
33 import org.osgi.framework.BundleContext;
34 import org.osgi.framework.ServiceRegistration;
35 import org.osgi.service.cm.ConfigurationException;
36 import org.osgi.service.cm.ManagedServiceFactory;
37 import org.osgi.service.component.ComponentContext;
38 import org.osgi.service.component.annotations.Activate;
39 import org.osgi.service.component.annotations.Component;
40 import org.osgi.service.component.annotations.Reference;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import java.lang.management.ManagementFactory;
45 import java.net.URI;
46 import java.net.URISyntaxException;
47 import java.util.Dictionary;
48 import java.util.Map;
49 import java.util.Objects;
50 import java.util.concurrent.ConcurrentHashMap;
51
52 import javax.management.MalformedObjectNameException;
53 import javax.management.ObjectName;
54
55
56
57
58 @Component(
59 immediate = true,
60 service = ManagedServiceFactory.class,
61 property = {
62 "service.pid=org.opencastproject.userdirectory.moodle",
63 "service.description=Provides Moodle user directory instances"
64 }
65 )
66 public class MoodleUserProviderFactory implements ManagedServiceFactory {
67
68
69
70 public static final String PID = "org.opencastproject.userdirectory.moodle";
71
72
73
74
75 private static final Logger logger = LoggerFactory.getLogger(MoodleUserProviderFactory.class);
76
77
78
79
80 private static final String ORGANIZATION_KEY = "org.opencastproject.userdirectory.moodle.org";
81
82
83
84
85 private static final String MOODLE_URL_KEY = "org.opencastproject.userdirectory.moodle.url";
86
87
88
89
90 private static final String MOODLE_TOKEN_KEY = "org.opencastproject.userdirectory.moodle.token";
91
92
93
94
95 private static final String CACHE_SIZE = "org.opencastproject.userdirectory.moodle.cache.size";
96
97
98
99
100 private static final String CACHE_EXPIRATION = "org.opencastproject.userdirectory.moodle.cache.expiration";
101
102
103
104
105 private static final String GROUP_ROLES_KEY = "org.opencastproject.userdirectory.moodle.group.roles.enabled";
106
107
108
109
110 private static final String COURSE_PATTERN_KEY = "org.opencastproject.userdirectory.moodle.course.pattern";
111
112
113
114
115 private static final String USER_PATTERN_KEY = "org.opencastproject.userdirectory.moodle.user.pattern";
116
117
118
119
120 private static final String GROUP_PATTERN_KEY = "org.opencastproject.userdirectory.moodle.group.pattern";
121
122
123 private static final String LOWERCASE_USERNAME = "org.opencastproject.userdirectory.moodle.user.lowercase.conversion";
124
125
126
127
128 private static final String CONTEXT_ROLE_PREFIX = "org.opencastproject.userdirectory.moodle.context.role.prefix";
129
130
131
132
133 protected BundleContext bundleContext = null;
134
135
136
137
138 private Map<String, ServiceRegistration> providerRegistrations = new ConcurrentHashMap<String, ServiceRegistration>();
139
140
141
142
143 private OrganizationDirectoryService orgDirectory;
144
145
146
147
148
149
150
151
152
153 public static ObjectName getObjectName(String pid) throws MalformedObjectNameException, NullPointerException {
154 return new ObjectName(pid + ":type=MoodleRequests");
155 }
156
157
158
159
160 @Reference
161 public void setOrgDirectory(OrganizationDirectoryService orgDirectory) {
162 this.orgDirectory = orgDirectory;
163 }
164
165
166
167
168
169
170 @Activate
171 public void activate(ComponentContext cc) {
172 logger.debug("Activate MoodleUserProviderFactory");
173 this.bundleContext = cc.getBundleContext();
174 }
175
176
177
178
179
180
181 @Override
182 public String getName() {
183 return PID;
184 }
185
186
187
188
189
190
191 @Override
192 public void updated(String pid, Dictionary properties) throws ConfigurationException {
193 logger.debug("updated MoodleUserProviderFactory");
194
195 String adminUserName = StringUtils.trimToNull(
196 bundleContext.getProperty(SecurityConstants.GLOBAL_ADMIN_USER_PROPERTY)
197 );
198
199 String organization = (String) properties.get(ORGANIZATION_KEY);
200 if (StringUtils.isBlank(organization)) {
201 throw new ConfigurationException(ORGANIZATION_KEY, "is not set");
202 }
203
204 String urlStr = (String) properties.get(MOODLE_URL_KEY);
205 URI url;
206 if (StringUtils.isBlank(urlStr)) {
207 throw new ConfigurationException(MOODLE_URL_KEY, "is not set");
208 }
209 try {
210 url = new URI(urlStr);
211 } catch (URISyntaxException e) {
212 throw new ConfigurationException(MOODLE_URL_KEY, "not a URL");
213 }
214
215 String token = (String) properties.get(MOODLE_TOKEN_KEY);
216 if (StringUtils.isBlank(token)) {
217 throw new ConfigurationException(MOODLE_TOKEN_KEY, "is not set");
218 }
219
220 final String groupRolesStr = (String) properties.get(GROUP_ROLES_KEY);
221 final boolean groupRoles = BooleanUtils.toBoolean(groupRolesStr);
222
223 String coursePattern = (String) properties.get(COURSE_PATTERN_KEY);
224 String userPattern = (String) properties.get(USER_PATTERN_KEY);
225 String groupPattern = (String) properties.get(GROUP_PATTERN_KEY);
226 final boolean lowercaseUsername = BooleanUtils.toBoolean((String) properties.get(LOWERCASE_USERNAME));
227
228 final String contextRolePrefix = Objects.toString(properties.get(CONTEXT_ROLE_PREFIX), "");
229
230 int cacheSize = 1000;
231 try {
232 if (properties.get(CACHE_SIZE) != null) {
233 cacheSize = Integer.parseInt(properties.get(CACHE_SIZE).toString());
234 }
235 } catch (NumberFormatException e) {
236 logger.warn("{} could not be loaded, default value is used: {}", CACHE_SIZE, cacheSize);
237 }
238
239 int cacheExpiration = 60;
240 try {
241 if (properties.get(CACHE_EXPIRATION) != null) {
242 cacheExpiration = Integer.parseInt(properties.get(CACHE_EXPIRATION).toString());
243 }
244 } catch (NumberFormatException e) {
245 logger.warn("{} could not be loaded, default value is used: {}", CACHE_EXPIRATION, cacheExpiration);
246 }
247
248
249 ServiceRegistration existingRegistration = providerRegistrations.remove(pid);
250 if (existingRegistration != null) {
251 existingRegistration.unregister();
252 }
253
254 Organization org;
255 try {
256 org = orgDirectory.getOrganization(organization);
257 } catch (NotFoundException e) {
258 logger.warn("Organization {} not found!", organization);
259 throw new ConfigurationException(ORGANIZATION_KEY, "not found");
260 }
261
262 logger.debug("creating new MoodleUserProviderInstance for pid=" + pid);
263 MoodleUserProviderInstance provider = new MoodleUserProviderInstance(pid, new MoodleWebServiceImpl(url, token), org,
264 coursePattern, userPattern, groupPattern, groupRoles, cacheSize, cacheExpiration, adminUserName,
265 lowercaseUsername, contextRolePrefix);
266
267 providerRegistrations.put(pid, bundleContext.registerService(UserProvider.class.getName(), provider, null));
268 providerRegistrations.put(pid, bundleContext.registerService(RoleProvider.class.getName(), provider, null));
269 }
270
271
272
273
274
275
276 @Override
277 public void deleted(String pid) {
278 logger.debug("delete MoodleUserProviderInstance for pid=" + pid);
279 ServiceRegistration registration = providerRegistrations.remove(pid);
280 if (registration != null) {
281 registration.unregister();
282 try {
283 ManagementFactory.getPlatformMBeanServer().unregisterMBean(MoodleUserProviderFactory.getObjectName(pid));
284 } catch (Exception e) {
285 logger.warn("Unable to unregister mbean for pid='{}': {}", pid, e.getMessage());
286 }
287 }
288 }
289 }