LCOV - code coverage report
Current view: top level - httpd/auth/ldap - LdapLoginServlet.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 8 65 12.3 %
Date: 2022-11-19 15:00:39 Functions: 2 5 40.0 %

          Line data    Source code
       1             : // Copyright (C) 2009 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.httpd.auth.ldap;
      16             : 
      17             : import static java.nio.charset.StandardCharsets.UTF_8;
      18             : 
      19             : import com.google.common.base.MoreObjects;
      20             : import com.google.common.base.Strings;
      21             : import com.google.common.flogger.FluentLogger;
      22             : import com.google.gerrit.common.Nullable;
      23             : import com.google.gerrit.extensions.registration.DynamicItem;
      24             : import com.google.gerrit.httpd.CanonicalWebUrl;
      25             : import com.google.gerrit.httpd.HtmlDomUtil;
      26             : import com.google.gerrit.httpd.LoginUrlToken;
      27             : import com.google.gerrit.httpd.WebSession;
      28             : import com.google.gerrit.httpd.template.SiteHeaderFooter;
      29             : import com.google.gerrit.server.account.AccountException;
      30             : import com.google.gerrit.server.account.AccountManager;
      31             : import com.google.gerrit.server.account.AccountUserNameException;
      32             : import com.google.gerrit.server.account.AuthRequest;
      33             : import com.google.gerrit.server.account.AuthResult;
      34             : import com.google.gerrit.server.account.AuthenticationFailedException;
      35             : import com.google.gerrit.server.auth.AuthenticationUnavailableException;
      36             : import com.google.gerrit.util.http.CacheHeaders;
      37             : import com.google.inject.Inject;
      38             : import com.google.inject.Singleton;
      39             : import java.io.IOException;
      40             : import javax.servlet.ServletException;
      41             : import javax.servlet.ServletOutputStream;
      42             : import javax.servlet.http.HttpServlet;
      43             : import javax.servlet.http.HttpServletRequest;
      44             : import javax.servlet.http.HttpServletResponse;
      45             : import org.w3c.dom.Document;
      46             : import org.w3c.dom.Element;
      47             : 
      48             : /** Handles username/password based authentication against the directory. */
      49             : @Singleton
      50             : class LdapLoginServlet extends HttpServlet {
      51             :   private static final long serialVersionUID = 1L;
      52             : 
      53           1 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      54             : 
      55             :   private final AccountManager accountManager;
      56             :   private final DynamicItem<WebSession> webSession;
      57             :   private final CanonicalWebUrl urlProvider;
      58             :   private final SiteHeaderFooter headers;
      59             :   private final AuthRequest.Factory authRequestFactory;
      60             : 
      61             :   @Inject
      62             :   LdapLoginServlet(
      63             :       AccountManager accountManager,
      64             :       DynamicItem<WebSession> webSession,
      65             :       CanonicalWebUrl urlProvider,
      66             :       SiteHeaderFooter headers,
      67           1 :       AuthRequest.Factory authRequestFactory) {
      68           1 :     this.accountManager = accountManager;
      69           1 :     this.webSession = webSession;
      70           1 :     this.urlProvider = urlProvider;
      71           1 :     this.headers = headers;
      72           1 :     this.authRequestFactory = authRequestFactory;
      73           1 :   }
      74             : 
      75             :   private void sendForm(
      76             :       HttpServletRequest req, HttpServletResponse res, @Nullable String errorMessage)
      77             :       throws IOException {
      78           0 :     String self = req.getRequestURI();
      79           0 :     String cancel = MoreObjects.firstNonNull(urlProvider.get(req), "/");
      80           0 :     cancel += LoginUrlToken.getToken(req);
      81             : 
      82           0 :     Document doc = headers.parse(LdapLoginServlet.class, "LoginForm.html");
      83           0 :     HtmlDomUtil.find(doc, "hostName").setTextContent(req.getServerName());
      84           0 :     HtmlDomUtil.find(doc, "login_form").setAttribute("action", self);
      85           0 :     HtmlDomUtil.find(doc, "cancel_link").setAttribute("href", cancel);
      86             : 
      87           0 :     Element emsg = HtmlDomUtil.find(doc, "error_message");
      88           0 :     if (Strings.isNullOrEmpty(errorMessage)) {
      89           0 :       emsg.getParentNode().removeChild(emsg);
      90             :     } else {
      91           0 :       emsg.setTextContent(errorMessage);
      92             :     }
      93             : 
      94           0 :     byte[] bin = HtmlDomUtil.toUTF8(doc);
      95           0 :     res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      96           0 :     res.setContentType("text/html");
      97           0 :     res.setCharacterEncoding(UTF_8.name());
      98           0 :     res.setContentLength(bin.length);
      99           0 :     try (ServletOutputStream out = res.getOutputStream()) {
     100           0 :       out.write(bin);
     101             :     }
     102           0 :   }
     103             : 
     104             :   @Override
     105             :   protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
     106           0 :     sendForm(req, res, null);
     107           0 :   }
     108             : 
     109             :   @Override
     110             :   protected void doPost(HttpServletRequest req, HttpServletResponse res)
     111             :       throws ServletException, IOException {
     112           0 :     req.setCharacterEncoding(UTF_8.name());
     113           0 :     String username = Strings.nullToEmpty(req.getParameter("username")).trim();
     114           0 :     String password = Strings.nullToEmpty(req.getParameter("password"));
     115           0 :     String remember = Strings.nullToEmpty(req.getParameter("rememberme"));
     116           0 :     if (username.isEmpty() || password.isEmpty()) {
     117           0 :       sendForm(req, res, "Invalid username or password.");
     118           0 :       return;
     119             :     }
     120             : 
     121           0 :     AuthRequest areq = authRequestFactory.createForUser(username);
     122           0 :     areq.setPassword(password);
     123             : 
     124             :     AuthResult ares;
     125             :     try {
     126           0 :       ares = accountManager.authenticate(areq);
     127           0 :     } catch (AccountUserNameException e) {
     128           0 :       sendForm(req, res, e.getMessage());
     129           0 :       return;
     130           0 :     } catch (AuthenticationUnavailableException e) {
     131           0 :       sendForm(req, res, "Authentication unavailable at this time.");
     132           0 :       return;
     133           0 :     } catch (AuthenticationFailedException e) {
     134             :       // This exception is thrown if the user provided wrong credentials, we don't need to log a
     135             :       // stacktrace for it.
     136           0 :       logger.atWarning().log("'%s' failed to sign in: %s", username, e.getMessage());
     137           0 :       sendForm(req, res, "Invalid username or password.");
     138           0 :       return;
     139           0 :     } catch (AccountException e) {
     140           0 :       logger.atWarning().withCause(e).log("'%s' failed to sign in", username);
     141           0 :       sendForm(req, res, "Authentication failed.");
     142           0 :       return;
     143           0 :     } catch (RuntimeException e) {
     144           0 :       logger.atSevere().withCause(e).log("LDAP authentication failed");
     145           0 :       sendForm(req, res, "Authentication unavailable at this time.");
     146           0 :       return;
     147           0 :     }
     148             : 
     149           0 :     StringBuilder dest = new StringBuilder();
     150           0 :     dest.append(urlProvider.get(req));
     151           0 :     dest.append(LoginUrlToken.getToken(req));
     152             : 
     153           0 :     CacheHeaders.setNotCacheable(res);
     154           0 :     webSession.get().login(ares, "1".equals(remember));
     155           0 :     res.sendRedirect(dest.toString());
     156           0 :   }
     157             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750