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.brightspace.client;
23
24 import org.opencastproject.userdirectory.brightspace.client.api.BrightspaceUser;
25 import org.opencastproject.userdirectory.brightspace.client.api.OrgUnitItem;
26 import org.opencastproject.userdirectory.brightspace.client.api.OrgUnitResponse;
27 import org.opencastproject.userdirectory.brightspace.client.api.PagingInfo;
28 import org.opencastproject.userdirectory.brightspace.client.api.UsersResponse;
29
30 import com.fasterxml.jackson.databind.ObjectMapper;
31
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import java.io.BufferedReader;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.InputStreamReader;
39 import java.net.MalformedURLException;
40 import java.net.URI;
41 import java.net.URL;
42 import java.net.URLConnection;
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.Set;
46
47 import javax.net.ssl.HttpsURLConnection;
48
49 import be.ugent.brightspace.idkeyauth.AuthenticationSecurityFactory;
50 import be.ugent.brightspace.idkeyauth.ID2LAppContext;
51 import be.ugent.brightspace.idkeyauth.ID2LUserContext;
52
53 public class BrightspaceClientImpl implements BrightspaceClient {
54
55 private static final Logger logger = LoggerFactory.getLogger(BrightspaceClientImpl.class);
56
57 private static final String GET_USER_BY_USERNAME = "/d2l/api/lp/1.31/users/?UserName=";
58 private static final String GET_ALL_USERS = "/d2l/api/lp/1.31/users/";
59 private static final String GET_COURSES_BY_BRIGHTSPACE_USER_ID
60 = "/d2l/api/lp/1.31/enrollments/users/{brightspace-userid}/orgUnits/?orgUnitTypeId=3";
61 private static final String UNEXPECTED_JSON_RESPONSE = "The brightspace API returned a unexpected json response";
62 private static final String SUPER_ADMIN = "Super Administrator";
63 private static final String LTI_LEARNER_ROLE = "Learner";
64 private static final String LTI_INSTRUCTOR_ROLE = "Instructor";
65
66 private final String url;
67 private final String applicationId;
68 private final String applicationKey;
69 private final String systemUserId;
70 private final String systemUserKey;
71 private final ID2LUserContext userContext;
72
73 private final ObjectMapper objectMapper = new ObjectMapper();
74
75 public BrightspaceClientImpl(String url, String applicationId, String applicationKey, String systemUserId,
76 String systemUserKey) {
77 this.url = url;
78 this.applicationId = applicationId;
79 this.applicationKey = applicationKey;
80 this.systemUserId = systemUserId;
81 this.systemUserKey = systemUserKey;
82 userContext = createUserContext();
83 }
84
85 public BrightspaceUser findUser(String userName) throws BrightspaceClientException {
86 String request = GET_USER_BY_USERNAME + userName;
87
88 try {
89 String response = httpGetRequest(request);
90 logger.debug(response);
91
92 BrightspaceUser brightspaceUser = objectMapper
93 .readerFor(BrightspaceUser.class)
94 .readValue(response);
95 return brightspaceUser;
96 } catch (BrightspaceNotFoundException nfe) {
97 return null;
98 } catch (IOException e) {
99 logger.debug(e.toString());
100 throw new BrightspaceClientException(UNEXPECTED_JSON_RESPONSE, e);
101 }
102 }
103
104 @Override public List<String> getRolesFromBrightspace(String userid, Set<String> instructorRoles)
105 throws BrightspaceClientException {
106 logger.debug("Retrieving subscribed courses for user: {}", userid);
107
108 boolean hasMoreItems;
109 String bookmark = null;
110 List<String> roleList = new ArrayList<>();
111 try {
112 do {
113 String request = composePagedUrl(bookmark, userid);
114 OrgUnitResponse orgUnitPage = findOrgUnitPage(request);
115 if (SUPER_ADMIN.equals(orgUnitPage.getItems().get(0).getRole().getName())) {
116 roleList.add("BRIGHTSPACE_ADMIN");
117 return roleList;
118 }
119
120 for (OrgUnitItem course: orgUnitPage.getItems()) {
121 String brightspaceRole = course.getRole().getName();
122 String ltiRole = instructorRoles.contains(brightspaceRole) ? LTI_INSTRUCTOR_ROLE : LTI_LEARNER_ROLE;
123 String opencastRole = String.format("%s_%s", course.getOrgUnit().getId(), ltiRole);
124 roleList.add(opencastRole);
125 }
126
127 PagingInfo pagingInfo = orgUnitPage.getPagingInfo();
128 hasMoreItems = pagingInfo.hasMoreItems();
129 bookmark = pagingInfo.getBookmark();
130
131 } while (hasMoreItems);
132
133 return roleList;
134 } catch (BrightspaceClientException e) {
135 logger.warn("Exception getting site/role membership for brightspace user {}", userid, e);
136 throw new BrightspaceClientException(UNEXPECTED_JSON_RESPONSE, e);
137 }
138
139 }
140
141 @Override
142 public List<BrightspaceUser> findAllUsers() throws BrightspaceClientException {
143 try {
144 String response = httpGetRequest(GET_ALL_USERS);
145 UsersResponse usersResponse = objectMapper.readValue(response, UsersResponse.class);
146 return usersResponse.getItems();
147 } catch (IOException e) {
148 throw new BrightspaceClientException(UNEXPECTED_JSON_RESPONSE, e);
149 } catch (BrightspaceNotFoundException nfe) {
150 throw new BrightspaceClientException(UNEXPECTED_JSON_RESPONSE, nfe);
151 }
152
153 }
154
155 public String getURL() {
156 return this.url;
157 }
158
159 private String httpGetRequest(String request) throws BrightspaceClientException, BrightspaceNotFoundException {
160 URL url = createUrl(request);
161
162 try {
163 HttpsURLConnection urlConnection = (HttpsURLConnection) getURLConnection(url);
164
165 if (urlConnection.getResponseCode() == 404) {
166 logger.debug("Not found, 404 response");
167 throw new BrightspaceNotFoundException("not found");
168 }
169
170 InputStream inputStream = urlConnection.getInputStream();
171 return readInputStream(inputStream);
172 } catch (IOException io) {
173 logger.warn("error in brightspace data fetching", io);
174 throw new BrightspaceClientException("could not read response");
175 }
176 }
177
178 private URL createUrl(String request) throws BrightspaceClientException {
179 URI uri = userContext.createAuthenticatedUri(request, "GET");
180 URL url;
181 try {
182 url = uri.toURL();
183 } catch (MalformedURLException mue) {
184 throw new BrightspaceClientException("url was malformed", mue);
185 }
186 logger.debug("about to make GET request to : {}", uri);
187 return url;
188 }
189
190 private ID2LUserContext createUserContext() {
191 ID2LAppContext securityContext = AuthenticationSecurityFactory
192 .createSecurityContext(applicationId, applicationKey, url);
193 return securityContext.createUserContext(systemUserId, systemUserKey);
194 }
195
196 private URLConnection getURLConnection(URL url) throws BrightspaceClientException {
197 try {
198 HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
199 urlConnection.setRequestMethod("GET");
200 urlConnection.setRequestProperty("Content-Type", "application/json");
201 urlConnection.setRequestProperty("Accept-Charset", "utf-8");
202 urlConnection.setDoInput(true);
203 urlConnection.setDoOutput(true);
204 return urlConnection;
205 } catch (IOException ioe) {
206 throw new BrightspaceClientException("Brightspace api unreachable", ioe);
207 }
208 }
209
210 private String readInputStream(InputStream inputStream) throws BrightspaceClientException {
211 StringBuilder content = new StringBuilder();
212 try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
213 String inputline;
214 while ((inputline = bufferedReader.readLine()) != null) {
215 content.append(inputline);
216 }
217 logger.debug("call to brightspace api: {}", content);
218 return content.toString();
219 } catch (IOException io) {
220 throw new BrightspaceClientException("Could not read response", io);
221 }
222
223 }
224
225 private OrgUnitResponse findOrgUnitPage(String request) throws BrightspaceClientException {
226 try {
227 String response = httpGetRequest(request);
228 return objectMapper.readValue(response, OrgUnitResponse.class);
229 } catch (IOException e) {
230 logger.error(UNEXPECTED_JSON_RESPONSE);
231 throw new BrightspaceClientException(UNEXPECTED_JSON_RESPONSE, e);
232 } catch (BrightspaceNotFoundException nfe) {
233 logger.error(UNEXPECTED_JSON_RESPONSE);
234 throw new BrightspaceClientException(UNEXPECTED_JSON_RESPONSE, nfe);
235 }
236 }
237
238 private String composePagedUrl(String bookmark, String brightspaceUserId) {
239 String request = GET_COURSES_BY_BRIGHTSPACE_USER_ID.replaceAll("\\{\\S+}", brightspaceUserId);
240 if (bookmark != null) {
241 request += "&bookmark=" + bookmark;
242 }
243 return request;
244 }
245 }