1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.user2;
20
21 import static org.mycore.user2.utils.MCRUserTransformer.JAXB_CONTEXT;
22
23 import java.io.IOException;
24 import java.net.URLEncoder;
25 import java.nio.charset.StandardCharsets;
26 import java.text.DateFormat;
27 import java.text.ParseException;
28 import java.text.SimpleDateFormat;
29 import java.util.Collections;
30 import java.util.Date;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.TimeZone;
36 import java.util.stream.Collectors;
37
38 import org.apache.logging.log4j.LogManager;
39 import org.apache.logging.log4j.Logger;
40 import org.jdom2.Attribute;
41 import org.jdom2.Document;
42 import org.jdom2.Element;
43 import org.jdom2.filter.Filters;
44 import org.jdom2.xpath.XPathExpression;
45 import org.jdom2.xpath.XPathFactory;
46 import org.mycore.access.MCRAccessManager;
47 import org.mycore.common.MCRSessionMgr;
48 import org.mycore.common.MCRSystemUserInformation;
49 import org.mycore.common.config.MCRConfiguration2;
50 import org.mycore.common.content.MCRJAXBContent;
51 import org.mycore.common.content.MCRJDOMContent;
52 import org.mycore.datamodel.common.MCRISO8601Date;
53 import org.mycore.frontend.servlets.MCRServlet;
54 import org.mycore.frontend.servlets.MCRServletJob;
55 import org.mycore.services.i18n.MCRTranslation;
56 import org.mycore.user2.utils.MCRUserTransformer;
57
58 import jakarta.servlet.http.HttpServletRequest;
59 import jakarta.servlet.http.HttpServletResponse;
60
61
62
63
64
65
66
67
68 public class MCRUserServlet extends MCRServlet {
69 private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC");
70
71 private static final long serialVersionUID = 1L;
72
73
74 private static final Logger LOGGER = LogManager.getLogger(MCRUserServlet.class);
75
76
77
78
79
80
81 public void doGetPost(MCRServletJob job) throws Exception {
82 HttpServletRequest req = job.getRequest();
83 HttpServletResponse res = job.getResponse();
84 if (forbidIfGuest(res)) {
85 return;
86 }
87 String action = req.getParameter("action");
88 String uid = req.getParameter("id");
89 MCRUser user;
90
91 if ((uid == null) || (uid.trim().length() == 0)) {
92 user = MCRUserManager.getCurrentUser();
93 uid = user != null ? String.valueOf(user.getUserID()) : null;
94 if (!(user instanceof MCRTransientUser)) {
95
96 user = MCRUserManager.getUser(uid);
97 }
98 } else {
99 user = MCRUserManager.getUser(uid);
100 }
101
102 if ("show".equals(action)) {
103 showUser(req, res, user, uid);
104 } else if ("save".equals(action)) {
105 saveUser(req, res);
106 } else if ("saveCurrentUser".equals(action)) {
107 saveCurrentUser(req, res);
108 } else if ("changeMyPassword".equals(action)) {
109 redirectToPasswordChangePage(req, res);
110 } else if ("password".equals(action)) {
111 changePassword(req, res, user, uid);
112 } else if ("delete".equals(action)) {
113 deleteUser(req, res, user);
114 } else {
115 listUsers(req, res);
116 }
117 }
118
119 private void redirectToPasswordChangePage(HttpServletRequest req, HttpServletResponse res) throws Exception {
120 MCRUser currentUser = MCRUserManager.getCurrentUser();
121 if (!checkUserIsNotNull(res, currentUser, null)) {
122 return;
123 }
124 if (checkUserIsLocked(res, currentUser) || checkUserIsDisabled(res, currentUser)) {
125 return;
126 }
127 String url = currentUser.getRealm().getPasswordChangeURL();
128 if (url == null) {
129 String msg = MCRTranslation.translate("component.user2.UserServlet.missingRealPasswortChangeURL",
130 currentUser.getRealmID());
131 res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
132 } else {
133 res.sendRedirect(url);
134 }
135 }
136
137 private static boolean checkUserIsNotNull(HttpServletResponse res, MCRUser currentUser, String userID)
138 throws IOException {
139 if (currentUser == null) {
140 String uid = userID == null ? MCRSessionMgr.getCurrentSession().getUserInformation().getUserID() : userID;
141 String msg = MCRTranslation.translate("component.user2.UserServlet.currentUserUnknown", uid);
142 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
143 return false;
144 }
145 return true;
146 }
147
148 private static boolean checkUserIsLocked(HttpServletResponse res, MCRUser currentUser) throws IOException {
149 if (currentUser.isLocked()) {
150 String userName = currentUser.getUserID();
151 String msg = MCRTranslation.translate("component.user2.UserServlet.isLocked", userName);
152 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
153 return true;
154 }
155 return false;
156 }
157
158 private static boolean checkUserIsDisabled(HttpServletResponse res, MCRUser currentUser) throws IOException {
159 if (currentUser.isDisabled()) {
160 String userName = currentUser.getUserID();
161 String msg = MCRTranslation.translate("component.user2.UserServlet.isDisabled", userName);
162 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
163 return true;
164 }
165 return false;
166 }
167
168 private static boolean forbidIfGuest(HttpServletResponse res) throws IOException {
169 if (MCRSessionMgr.getCurrentSession().getUserInformation().getUserID()
170 .equals(MCRSystemUserInformation.getGuestInstance().getUserID())) {
171 String msg = MCRTranslation.translate("component.user2.UserServlet.noGuestAction");
172 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
173 return true;
174 }
175 return false;
176 }
177
178
179
180
181
182 private void showUser(HttpServletRequest req, HttpServletResponse res, MCRUser user, String uid) throws Exception {
183 MCRUser currentUser = MCRUserManager.getCurrentUser();
184 if (!checkUserIsNotNull(res, currentUser, null) || !checkUserIsNotNull(res, user, uid)) {
185 return;
186 }
187 boolean allowed = MCRAccessManager.checkPermission(MCRUser2Constants.USER_ADMIN_PERMISSION)
188 || currentUser.equals(user) || currentUser.equals(user.getOwner());
189 if (!allowed) {
190 String msg = MCRTranslation.translate("component.user2.UserServlet.noAdminPermission");
191 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
192 return;
193 }
194
195 LOGGER.info("show user {} {} {}", user.getUserID(), user.getUserName(), user.getRealmID());
196 getLayoutService().doLayout(req, res, getContent(user));
197 }
198
199
200
201
202
203 public static boolean checkUserName(String userName) {
204 String realmID = MCRRealmFactory.getLocalRealm().getID();
205
206
207 if ((userName == null) || (realmID == null)) {
208 return true;
209 }
210
211
212 return !MCRUserManager.exists(userName, realmID);
213 }
214
215 private void saveCurrentUser(HttpServletRequest req, HttpServletResponse res) throws IOException {
216 MCRUser currentUser = MCRUserManager.getCurrentUser();
217 if (!checkUserIsNotNull(res, currentUser, null)) {
218 return;
219 }
220 if (checkUserIsLocked(res, currentUser) || checkUserIsDisabled(res, currentUser)) {
221 return;
222 }
223 if (!currentUser.hasNoOwner() && currentUser.isLocked()) {
224 res.sendError(HttpServletResponse.SC_FORBIDDEN);
225 return;
226 }
227
228 Document doc = (Document) (req.getAttribute("MCRXEditorSubmission"));
229 Element u = doc.getRootElement();
230 updateBasicUserInfo(u, currentUser);
231 MCRUserManager.updateUser(currentUser);
232
233 res.sendRedirect(res.encodeRedirectURL("MCRUserServlet?action=show"));
234 }
235
236
237
238
239
240
241
242 private void saveUser(HttpServletRequest req, HttpServletResponse res) throws Exception {
243 MCRUser currentUser = MCRUserManager.getCurrentUser();
244 if (!checkUserIsNotNull(res, currentUser, null)) {
245 return;
246 }
247 boolean hasAdminPermission = MCRAccessManager.checkPermission(MCRUser2Constants.USER_ADMIN_PERMISSION);
248 boolean allowed = hasAdminPermission
249 || MCRAccessManager.checkPermission(MCRUser2Constants.USER_CREATE_PERMISSION);
250 if (!allowed) {
251 String msg = MCRTranslation.translate("component.user2.UserServlet.noCreatePermission");
252 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
253 return;
254 }
255
256 Document doc = (Document) (req.getAttribute("MCRXEditorSubmission"));
257 Element u = doc.getRootElement();
258 String userName = u.getAttributeValue("name");
259
260 String realmID = MCRRealmFactory.getLocalRealm().getID();
261 if (hasAdminPermission) {
262 realmID = u.getAttributeValue("realm");
263 }
264
265 MCRUser user;
266 boolean userExists = MCRUserManager.exists(userName, realmID);
267 if (!userExists) {
268 user = new MCRUser(userName, realmID);
269 LOGGER.info("create new user {} {}", userName, realmID);
270
271
272 String pwd = u.getChildText("password");
273 if ((pwd != null) && (pwd.trim().length() > 0) && user.getRealm().equals(MCRRealmFactory.getLocalRealm())) {
274 MCRUserManager.updatePasswordHashToSHA256(user, pwd);
275 }
276 } else {
277 user = MCRUserManager.getUser(userName, realmID);
278 if (!(hasAdminPermission || currentUser.equals(user) || currentUser.equals(user.getOwner()))) {
279 res.sendError(HttpServletResponse.SC_FORBIDDEN);
280 return;
281 }
282 }
283
284 XPathExpression<Attribute> hintPath = XPathFactory.instance().compile("password/@hint", Filters.attribute());
285 Attribute hintAttr = hintPath.evaluateFirst(u);
286 String hint = hintAttr == null ? null : hintAttr.getValue();
287 if ((hint != null) && (hint.trim().length() == 0)) {
288 hint = null;
289 }
290 user.setHint(hint);
291
292 updateBasicUserInfo(u, user);
293
294 if (hasAdminPermission) {
295 boolean locked = "true".equals(u.getAttributeValue("locked"));
296 user.setLocked(locked);
297
298 boolean disabled = "true".equals(u.getAttributeValue("disabled"));
299 user.setDisabled(disabled);
300
301 Element o = u.getChild("owner");
302 if (o != null && !o.getAttributes().isEmpty()) {
303 String ownerName = o.getAttributeValue("name");
304 String ownerRealm = o.getAttributeValue("realm");
305 MCRUser owner = MCRUserManager.getUser(ownerName, ownerRealm);
306 if (!checkUserIsNotNull(res, owner, ownerName + "@" + ownerRealm)) {
307 return;
308 }
309 user.setOwner(owner);
310 } else {
311 user.setOwner(null);
312 }
313 String validUntilText = u.getChildTextTrim("validUntil");
314 if (validUntilText == null || validUntilText.length() == 0) {
315 user.setValidUntil(null);
316 } else {
317
318 String dateInUTC = validUntilText;
319 if (validUntilText.length() == 10) {
320 dateInUTC = convertToUTC(validUntilText, "yyyy-MM-dd");
321 }
322
323 MCRISO8601Date date = new MCRISO8601Date(dateInUTC);
324 user.setValidUntil(date.getDate());
325 }
326 } else {
327 user.setRealm(MCRRealmFactory.getLocalRealm());
328 user.setOwner(currentUser);
329 }
330 Element gs = u.getChild("roles");
331 if (gs != null) {
332 user.getSystemRoleIDs().clear();
333 user.getExternalRoleIDs().clear();
334 List<Element> groupList = gs.getChildren("role");
335 for (Element group : groupList) {
336 String groupName = group.getAttributeValue("name");
337 if (hasAdminPermission || currentUser.isUserInRole(groupName)) {
338 user.assignRole(groupName);
339 } else {
340 LOGGER.warn("Current user {} has not the permission to add user to group {}",
341 currentUser.getUserID(), groupName);
342 }
343 }
344 }
345
346 if (userExists) {
347 MCRUserManager.updateUser(user);
348 } else {
349 MCRUserManager.createUser(user);
350 }
351
352 res.sendRedirect(res.encodeRedirectURL("MCRUserServlet?action=show&id="
353 + URLEncoder.encode(user.getUserID(), StandardCharsets.UTF_8)));
354 }
355
356 private String convertToUTC(String validUntilText, String format) throws ParseException {
357 DateFormat inputFormat = new SimpleDateFormat(format, Locale.ROOT);
358 inputFormat.setTimeZone(UTC_TIME_ZONE);
359 DateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.ROOT);
360
361 Date d = inputFormat.parse(validUntilText);
362 outputFormat.setTimeZone(UTC_TIME_ZONE);
363 return outputFormat.format(d);
364 }
365
366 private void updateBasicUserInfo(Element u, MCRUser user) {
367 String name = u.getChildText("realName");
368 if ((name != null) && (name.trim().length() == 0)) {
369 name = null;
370 }
371 user.setRealName(name);
372
373 String eMail = u.getChildText("eMail");
374 if ((eMail != null) && (eMail.trim().length() == 0)) {
375 eMail = null;
376 }
377 user.setEMail(eMail);
378
379 List<Element> attributeList = Optional.ofNullable(u.getChild("attributes"))
380 .map(attributes -> attributes.getChildren("attribute"))
381 .orElse(Collections.emptyList());
382 Set<MCRUserAttribute> newAttrs = attributeList.stream()
383 .map(a -> new MCRUserAttribute(a.getAttributeValue("name"), a.getAttributeValue("value")))
384 .collect(Collectors.toSet());
385 user.getAttributes().retainAll(newAttrs);
386 newAttrs.removeAll(user.getAttributes());
387 user.getAttributes().addAll(newAttrs);
388 }
389
390
391
392
393
394
395
396 private void changePassword(HttpServletRequest req, HttpServletResponse res, MCRUser user, String uid)
397 throws Exception {
398 MCRUser currentUser = MCRUserManager.getCurrentUser();
399 if (!checkUserIsNotNull(res, currentUser, null) || !checkUserIsNotNull(res, user, uid)) {
400 return;
401 }
402 boolean allowed = MCRAccessManager.checkPermission(MCRUser2Constants.USER_ADMIN_PERMISSION)
403 || currentUser.equals(user.getOwner())
404 || currentUser.equals(user) && !currentUser.isLocked();
405 if (!allowed) {
406 String msg = MCRTranslation.translate("component.user2.UserServlet.noAdminPermission");
407 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
408 return;
409 }
410
411 LOGGER.info("change password of user {} {} {}", user.getUserID(), user.getUserName(), user.getRealmID());
412
413 Document doc = (Document) (req.getAttribute("MCRXEditorSubmission"));
414 String password = doc.getRootElement().getChildText("password");
415 MCRUserManager.setPassword(user, password);
416
417 res.sendRedirect(res.encodeRedirectURL("MCRUserServlet?action=show&XSL.step=changedPassword&id="
418 + URLEncoder.encode(user.getUserID(), StandardCharsets.UTF_8)));
419 }
420
421
422
423
424
425
426 private void deleteUser(HttpServletRequest req, HttpServletResponse res, MCRUser user) throws Exception {
427 MCRUser currentUser = MCRUserManager.getCurrentUser();
428 boolean allowed = MCRAccessManager.checkPermission(MCRUser2Constants.USER_ADMIN_PERMISSION)
429 || currentUser.equals(user.getOwner());
430 if (!allowed) {
431 String msg = MCRTranslation.translate("component.user2.UserServlet.noAdminPermission");
432 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
433 return;
434 }
435
436 LOGGER.info("delete user {} {} {}", user.getUserID(), user.getUserName(), user.getRealmID());
437 MCRUserManager.deleteUser(user);
438 getLayoutService().doLayout(req, res, getContent(user));
439 }
440
441 private MCRJAXBContent<MCRUser> getContent(MCRUser user) {
442 return new MCRJAXBContent<>(JAXB_CONTEXT, user.getSafeCopy());
443 }
444
445
446
447
448
449
450
451
452
453
454
455
456 private void listUsers(HttpServletRequest req, HttpServletResponse res) throws Exception {
457 MCRUser currentUser = MCRUserManager.getCurrentUser();
458 List<MCRUser> ownUsers = MCRUserManager.listUsers(currentUser);
459 boolean hasAdminPermission = MCRAccessManager.checkPermission(MCRUser2Constants.USER_ADMIN_PERMISSION);
460 boolean allowed = hasAdminPermission
461 || MCRAccessManager.checkPermission(MCRUser2Constants.USER_CREATE_PERMISSION) || !ownUsers.isEmpty();
462 if (!allowed) {
463 String msg = MCRTranslation.translate("component.user2.UserServlet.noCreatePermission");
464 res.sendError(HttpServletResponse.SC_FORBIDDEN, msg);
465 return;
466 }
467
468 Element users = new Element("users");
469
470 List<MCRUser> results = null;
471 if (hasAdminPermission) {
472 String search = req.getParameter("search");
473 if ((search == null) || search.trim().length() == 0) {
474 search = null;
475 }
476
477 if (search != null) {
478 users.setAttribute("search", search);
479 search = "*" + search + "*";
480 }
481
482 LOGGER.info("search users like {}", search);
483
484 int max = MCRConfiguration2.getInt(MCRUser2Constants.CONFIG_PREFIX + "Users.MaxResults").orElse(100);
485 int num = MCRUserManager.countUsers(search, null, search, search);
486
487 if ((num < max) && (num > 0)) {
488 results = MCRUserManager.listUsers(search, null, search, search);
489 }
490 users.setAttribute("num", String.valueOf(num));
491 users.setAttribute("max", String.valueOf(max));
492 } else {
493 LOGGER.info("list owned users of {} {}", currentUser.getUserName(), currentUser.getRealmID());
494 results = ownUsers;
495 }
496
497 if (results != null) {
498 for (MCRUser user : results) {
499 Element u = MCRUserTransformer.buildBasicXML(user).detachRootElement();
500 addString(u, "realName", user.getRealName());
501 addString(u, "eMail", user.getEMailAddress());
502 users.addContent(u);
503 }
504 }
505
506 getLayoutService().doLayout(req, res, new MCRJDOMContent(users));
507 }
508
509 private void addString(Element parent, String name, String value) {
510 if ((value != null) && (value.trim().length() > 0)) {
511 parent.addContent(new Element(name).setText(value.trim()));
512 }
513 }
514 }