1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.user2.login;
20
21 import java.io.IOException;
22 import java.nio.charset.StandardCharsets;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.StringTokenizer;
27 import java.util.stream.Collectors;
28
29 import javax.xml.transform.TransformerException;
30
31 import org.apache.logging.log4j.LogManager;
32 import org.apache.logging.log4j.Logger;
33 import org.jdom2.Document;
34 import org.jdom2.Element;
35 import org.mycore.common.MCRSessionMgr;
36 import org.mycore.common.MCRSystemUserInformation;
37 import org.mycore.common.MCRUserInformation;
38 import org.mycore.common.config.MCRConfiguration2;
39 import org.mycore.common.content.MCRJAXBContent;
40 import org.mycore.common.content.MCRJDOMContent;
41 import org.mycore.frontend.MCRFrontendUtil;
42 import org.mycore.frontend.servlets.MCRServlet;
43 import org.mycore.frontend.servlets.MCRServletJob;
44 import org.mycore.frontend.support.MCRLogin.InputField;
45 import org.mycore.services.i18n.MCRTranslation;
46 import org.mycore.user2.MCRRealm;
47 import org.mycore.user2.MCRRealmFactory;
48 import org.mycore.user2.MCRUser;
49 import org.mycore.user2.MCRUser2Constants;
50 import org.mycore.user2.MCRUserManager;
51 import org.xml.sax.SAXException;
52
53 import jakarta.servlet.ServletException;
54 import jakarta.servlet.http.HttpServletRequest;
55 import jakarta.servlet.http.HttpServletResponse;
56 import jakarta.xml.bind.JAXBContext;
57 import jakarta.xml.bind.JAXBException;
58
59
60
61
62
63
64
65
66
67
68 public class MCRLoginServlet extends MCRServlet {
69 protected static final String REALM_URL_PARAMETER = "realm";
70
71 static final String HTTPS_ONLY_PROPERTY = MCRUser2Constants.CONFIG_PREFIX + "LoginHttpsOnly";
72
73 static final String ALLOWED_ROLES_PROPERTY = MCRUser2Constants.CONFIG_PREFIX + "LoginAllowedRoles";
74
75 private static final long serialVersionUID = 1L;
76
77 private static final String LOGIN_REDIRECT_URL_PARAMETER = "url";
78
79 private static final String LOGIN_REDIRECT_URL_KEY = "loginRedirectURL";
80
81 protected static final boolean LOCAL_LOGIN_SECURE_ONLY = MCRConfiguration2
82 .getOrThrow(HTTPS_ONLY_PROPERTY, Boolean::parseBoolean);
83
84 private static final List<String> ALLOWED_ROLES = MCRConfiguration2
85 .getString(MCRLoginServlet.ALLOWED_ROLES_PROPERTY)
86 .map(MCRConfiguration2::splitValue)
87 .map(s -> s.collect(Collectors.toList()))
88 .orElse(Collections.emptyList());
89
90 private static Logger LOGGER = LogManager.getLogger();
91
92 @Override
93 public void init() throws ServletException {
94 if (!LOCAL_LOGIN_SECURE_ONLY) {
95 LOGGER.warn("Login over unsecure connection is permitted. Set '" + HTTPS_ONLY_PROPERTY
96 + "=true' to prevent cleartext transmissions of passwords.");
97 }
98 super.init();
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 public void doGetPost(MCRServletJob job) throws Exception {
122 HttpServletRequest req = job.getRequest();
123 HttpServletResponse res = job.getResponse();
124
125 String action = req.getParameter("action");
126 String realm = req.getParameter(REALM_URL_PARAMETER);
127 job.getResponse().setHeader("Cache-Control", "no-cache");
128 job.getResponse().setHeader("Pragma", "no-cache");
129 job.getResponse().setHeader("Expires", "0");
130
131 if ("login".equals(action)) {
132 presentLoginForm(job);
133 } else if ("cancel".equals(action)) {
134 redirect(res);
135 } else if (realm != null) {
136 loginToRealm(req, res, req.getParameter(REALM_URL_PARAMETER));
137 } else {
138 chooseLoginMethod(req, res);
139 }
140 }
141
142
143
144
145
146 private void chooseLoginMethod(HttpServletRequest req, HttpServletResponse res) throws Exception {
147 storeURL(getReturnURL(req));
148
149 if ((getNumLoginOptions() == 1) && currentUserIsGuest()) {
150 redirectToUniqueRealm(req, res);
151 } else {
152 listRealms(req, res);
153 }
154 }
155
156 protected static String getReturnURL(HttpServletRequest req) {
157 String returnURL = req.getParameter(LOGIN_REDIRECT_URL_PARAMETER);
158 if (returnURL == null) {
159 String referer = req.getHeader("Referer");
160 returnURL = (referer != null) ? referer : req.getContextPath() + "/";
161 }
162 return returnURL;
163 }
164
165 private void redirectToUniqueRealm(HttpServletRequest req, HttpServletResponse res) throws Exception {
166 String realmID = MCRRealmFactory.listRealms().iterator().next().getID();
167 loginToRealm(req, res, realmID);
168 }
169
170 protected void presentLoginForm(MCRServletJob job)
171 throws IOException, TransformerException, SAXException, JAXBException {
172 HttpServletRequest req = job.getRequest();
173 HttpServletResponse res = job.getResponse();
174 if (LOCAL_LOGIN_SECURE_ONLY && !req.isSecure()) {
175 res.sendError(HttpServletResponse.SC_FORBIDDEN, getErrorI18N("component.user2.login", "httpsOnly"));
176 return;
177 }
178
179 String returnURL = getReturnURL(req);
180 String formAction = req.getRequestURI();
181 MCRLogin loginForm = new MCRLogin(MCRSessionMgr.getCurrentSession().getUserInformation(), returnURL,
182 formAction);
183 String uid = getProperty(req, "uid");
184 String pwd = getProperty(req, "pwd");
185 if (uid != null) {
186 MCRUser user = MCRUserManager.login(uid, pwd, ALLOWED_ROLES);
187 if (user == null) {
188 res.setStatus(HttpServletResponse.SC_BAD_REQUEST);
189 loginForm.setLoginFailed(true);
190 } else {
191
192
193 req.changeSessionId();
194 LOGGER.info("user {} logged in successfully.", uid);
195 res.sendRedirect(res.encodeRedirectURL(getReturnURL(req)));
196 return;
197 }
198 }
199 addFormFields(loginForm, job.getRequest().getParameter(REALM_URL_PARAMETER));
200 getLayoutService().doLayout(req, res, new MCRJAXBContent<>(JAXBContext.newInstance(MCRLogin.class), loginForm));
201 }
202
203 private void listRealms(HttpServletRequest req, HttpServletResponse res)
204 throws IOException, TransformerException, SAXException {
205 String redirectURL = getReturnURL(req);
206 Document realmsDoc = MCRRealmFactory.getRealmsDocument();
207 Element realms = realmsDoc.getRootElement();
208 addCurrentUserInfo(realms);
209 List<Element> realmList = realms.getChildren(REALM_URL_PARAMETER);
210 for (Element realm : realmList) {
211 String realmID = realm.getAttributeValue("id");
212 Element login = realm.getChild("login");
213 if (login != null) {
214 login.setAttribute("url", MCRRealmFactory.getRealm(realmID).getLoginURL(redirectURL));
215 }
216 }
217 getLayoutService().doLayout(req, res, new MCRJDOMContent(realmsDoc));
218 }
219
220 protected static void addFormFields(MCRLogin login, String loginToRealm) {
221 ArrayList<org.mycore.frontend.support.MCRLogin.InputField> fields = new ArrayList<>();
222 if (loginToRealm != null) {
223
224 MCRRealm realm = MCRRealmFactory.getRealm(loginToRealm);
225 InputField realmParameter = new InputField(realm.getRealmParameter(), loginToRealm, null, null, false,
226 true);
227 fields.add(realmParameter);
228 }
229 fields.add(new InputField("action", "login", null, null, false, true));
230 fields.add(new InputField("url", login.getReturnURL(), null, null, false, true));
231 String userNameText = MCRTranslation.translate("component.user2.login.form.userName");
232 fields.add(new InputField("uid", null, userNameText, userNameText, false, false));
233 String pwdText = MCRTranslation.translate("component.user2.login.form.password");
234 fields.add(new InputField("pwd", null, pwdText, pwdText, true, false));
235 login.getForm().getInput().addAll(fields);
236 }
237
238 static void addCurrentUserInfo(Element rootElement) {
239 MCRUserInformation userInfo = MCRSessionMgr.getCurrentSession().getUserInformation();
240 rootElement.setAttribute("user", userInfo.getUserID());
241 String realmId = (userInfo instanceof MCRUser) ? ((MCRUser) userInfo).getRealm().getLabel()
242 : userInfo.getUserAttribute(MCRRealm.USER_INFORMATION_ATTR);
243 if (realmId == null) {
244 realmId = MCRRealmFactory.getLocalRealm().getLabel();
245 }
246 rootElement.setAttribute(REALM_URL_PARAMETER, realmId);
247 rootElement.setAttribute("guest", String.valueOf(currentUserIsGuest()));
248 }
249
250 static void addCurrentUserInfo(MCRLogin login) {
251 MCRUserInformation userInfo = MCRSessionMgr.getCurrentSession().getUserInformation();
252 String realmId = (userInfo instanceof MCRUser) ? ((MCRUser) userInfo).getRealm().getLabel()
253 : userInfo.getUserAttribute(MCRRealm.USER_INFORMATION_ATTR);
254 if (realmId == null) {
255 realmId = MCRRealmFactory.getLocalRealm().getLabel();
256 }
257 login.setRealm(realmId);
258 }
259
260 private static boolean currentUserIsGuest() {
261 return MCRSessionMgr.getCurrentSession().getUserInformation().getUserID()
262 .equals(MCRSystemUserInformation.getGuestInstance().getUserID());
263 }
264
265 private int getNumLoginOptions() {
266 int numOptions = 0;
267 for (MCRRealm realm : MCRRealmFactory.listRealms()) {
268 numOptions++;
269 if (realm.getCreateURL() != null) {
270 numOptions++;
271 }
272 }
273 return numOptions;
274 }
275
276 private void loginToRealm(HttpServletRequest req, HttpServletResponse res, String realmID) throws Exception {
277 String redirectURL = getReturnURL(req);
278 storeURL(redirectURL);
279 MCRRealm realm = MCRRealmFactory.getRealm(realmID);
280 String loginURL = realm.getLoginURL(redirectURL);
281 res.sendRedirect(res.encodeRedirectURL(loginURL));
282 }
283
284
285
286
287
288 private void storeURL(String url) throws Exception {
289 if ((url == null) || (url.trim().length() == 0)) {
290 url = MCRFrontendUtil.getBaseURL();
291 } else if (url.startsWith(MCRFrontendUtil.getBaseURL()) && !url.equals(MCRFrontendUtil.getBaseURL())) {
292 String rest = url.substring(MCRFrontendUtil.getBaseURL().length());
293 url = MCRFrontendUtil.getBaseURL() + encodePath(rest);
294 }
295 LOGGER.info("Storing redirect URL to session: {}", url);
296 MCRSessionMgr.getCurrentSession().put(LOGIN_REDIRECT_URL_KEY, url);
297 }
298
299 private String encodePath(String path) throws Exception {
300 path = path.replace('\\', '/');
301
302 StringBuilder result = new StringBuilder();
303 StringTokenizer st = new StringTokenizer(path, " /?&=", true);
304
305 while (st.hasMoreTokens()) {
306 String token = st.nextToken();
307 switch (token) {
308 case " ":
309 result.append("%20");
310 break;
311 case "/":
312 case "?":
313 case "&":
314 case "=":
315 result.append(token);
316 break;
317 default:
318 result.append(java.net.URLEncoder.encode(token, StandardCharsets.UTF_8));
319 break;
320 }
321 }
322
323 return result.toString();
324 }
325
326
327
328
329 static void redirect(HttpServletResponse res) throws Exception {
330 String url = (String) (MCRSessionMgr.getCurrentSession().get(LOGIN_REDIRECT_URL_KEY));
331 if (url == null) {
332 LOGGER.warn("Could not get redirect URL from session.");
333 url = MCRFrontendUtil.getBaseURL();
334 }
335 LOGGER.info("Redirecting to url: {}", url);
336 res.sendRedirect(res.encodeRedirectURL(url));
337 }
338 }