LCOV - code coverage report
Current view: top level - httpd/auth/container - HttpLoginServlet.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 0 76 0.0 %
Date: 2022-11-19 15:00:39 Functions: 0 6 0.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.container;
      16             : 
      17             : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_EXTERNAL;
      18             : import static java.nio.charset.StandardCharsets.UTF_8;
      19             : 
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.common.PageLinks;
      22             : import com.google.gerrit.extensions.registration.DynamicItem;
      23             : import com.google.gerrit.httpd.CanonicalWebUrl;
      24             : import com.google.gerrit.httpd.HtmlDomUtil;
      25             : import com.google.gerrit.httpd.LoginUrlToken;
      26             : import com.google.gerrit.httpd.WebSession;
      27             : import com.google.gerrit.server.account.AccountException;
      28             : import com.google.gerrit.server.account.AccountManager;
      29             : import com.google.gerrit.server.account.AuthRequest;
      30             : import com.google.gerrit.server.account.AuthResult;
      31             : import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
      32             : import com.google.gerrit.server.config.AuthConfig;
      33             : import com.google.gerrit.util.http.CacheHeaders;
      34             : import com.google.inject.Inject;
      35             : import com.google.inject.Singleton;
      36             : import java.io.IOException;
      37             : import javax.servlet.ServletException;
      38             : import javax.servlet.ServletOutputStream;
      39             : import javax.servlet.http.HttpServlet;
      40             : import javax.servlet.http.HttpServletRequest;
      41             : import javax.servlet.http.HttpServletResponse;
      42             : import org.eclipse.jgit.errors.ConfigInvalidException;
      43             : import org.w3c.dom.Document;
      44             : import org.w3c.dom.Element;
      45             : import org.w3c.dom.Node;
      46             : import org.w3c.dom.NodeList;
      47             : 
      48             : /**
      49             :  * Initializes the user session if HTTP authentication is enabled.
      50             :  *
      51             :  * <p>If HTTP authentication has been enabled this servlet binds to {@code /login/} and initializes
      52             :  * the user session based on user information contained in the HTTP request.
      53             :  */
      54             : @Singleton
      55             : class HttpLoginServlet extends HttpServlet {
      56             :   private static final long serialVersionUID = 1L;
      57           0 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      58             : 
      59             :   private final DynamicItem<WebSession> webSession;
      60             :   private final CanonicalWebUrl urlProvider;
      61             :   private final AccountManager accountManager;
      62             :   private final HttpAuthFilter authFilter;
      63             :   private final AuthConfig authConfig;
      64             :   private final ExternalIdKeyFactory externalIdKeyFactory;
      65             :   private final AuthRequest.Factory authRequestFactory;
      66             : 
      67             :   @Inject
      68             :   HttpLoginServlet(
      69             :       final DynamicItem<WebSession> webSession,
      70             :       final CanonicalWebUrl urlProvider,
      71             :       final AccountManager accountManager,
      72             :       final HttpAuthFilter authFilter,
      73             :       final AuthConfig authConfig,
      74             :       final ExternalIdKeyFactory externalIdKeyFactory,
      75           0 :       final AuthRequest.Factory authRequestFactory) {
      76           0 :     this.webSession = webSession;
      77           0 :     this.urlProvider = urlProvider;
      78           0 :     this.accountManager = accountManager;
      79           0 :     this.authFilter = authFilter;
      80           0 :     this.authConfig = authConfig;
      81           0 :     this.externalIdKeyFactory = externalIdKeyFactory;
      82           0 :     this.authRequestFactory = authRequestFactory;
      83           0 :   }
      84             : 
      85             :   @Override
      86             :   protected void doGet(HttpServletRequest req, HttpServletResponse rsp)
      87             :       throws ServletException, IOException {
      88           0 :     final String token = LoginUrlToken.getToken(req);
      89             : 
      90           0 :     CacheHeaders.setNotCacheable(rsp);
      91           0 :     final String user = authFilter.getRemoteUser(req);
      92           0 :     if (user == null || "".equals(user)) {
      93           0 :       logger.atSevere().log(
      94             :           "Unable to authenticate user by %s request header."
      95             :               + " Check container or server configuration.",
      96           0 :           authFilter.getLoginHeader());
      97             : 
      98           0 :       final Document doc =
      99           0 :           HtmlDomUtil.parseFile( //
     100             :               HttpLoginServlet.class, "ConfigurationError.html");
     101             : 
     102           0 :       replace(doc, "loginHeader", authFilter.getLoginHeader());
     103           0 :       replace(doc, "ServerName", req.getServerName());
     104           0 :       replace(doc, "ServerPort", ":" + req.getServerPort());
     105           0 :       replace(doc, "ContextPath", req.getContextPath());
     106             : 
     107           0 :       final byte[] bin = HtmlDomUtil.toUTF8(doc);
     108           0 :       rsp.setStatus(HttpServletResponse.SC_FORBIDDEN);
     109           0 :       rsp.setContentType("text/html");
     110           0 :       rsp.setCharacterEncoding(UTF_8.name());
     111           0 :       rsp.setContentLength(bin.length);
     112           0 :       try (ServletOutputStream out = rsp.getOutputStream()) {
     113           0 :         out.write(bin);
     114             :       }
     115           0 :       return;
     116             :     }
     117             : 
     118           0 :     final AuthRequest areq = authRequestFactory.createForUser(user);
     119           0 :     areq.setDisplayName(authFilter.getRemoteDisplayname(req));
     120           0 :     areq.setEmailAddress(authFilter.getRemoteEmail(req));
     121             :     final AuthResult arsp;
     122             :     try {
     123           0 :       arsp = accountManager.authenticate(areq);
     124           0 :     } catch (AccountException e) {
     125           0 :       logger.atSevere().withCause(e).log("Unable to authenticate user \"%s\"", user);
     126           0 :       rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
     127           0 :       return;
     128           0 :     }
     129             : 
     130           0 :     String remoteExternalId = authFilter.getRemoteExternalIdToken(req);
     131           0 :     if (remoteExternalId != null) {
     132             :       try {
     133           0 :         logger.atFine().log(
     134             :             "Associating external identity \"%s\" to user \"%s\"", remoteExternalId, user);
     135           0 :         updateRemoteExternalId(arsp, remoteExternalId);
     136           0 :       } catch (AccountException | ConfigInvalidException e) {
     137           0 :         logger.atSevere().withCause(e).log(
     138             :             "Unable to associate external identity \"%s\" to user \"%s\"", remoteExternalId, user);
     139           0 :         rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
     140           0 :         return;
     141           0 :       }
     142             :     }
     143             : 
     144           0 :     final StringBuilder rdr = new StringBuilder();
     145           0 :     if (arsp.isNew() && authConfig.getRegisterPageUrl() != null) {
     146           0 :       rdr.append(authConfig.getRegisterPageUrl());
     147             :     } else {
     148           0 :       rdr.append(urlProvider.get(req));
     149           0 :       if (arsp.isNew() && !token.startsWith(PageLinks.REGISTER + "/")) {
     150           0 :         rdr.append('#' + PageLinks.REGISTER);
     151             :       }
     152           0 :       rdr.append(token);
     153             :     }
     154             : 
     155           0 :     webSession.get().login(arsp, true /* persistent cookie */);
     156           0 :     rsp.sendRedirect(rdr.toString());
     157           0 :   }
     158             : 
     159             :   private void updateRemoteExternalId(AuthResult arsp, String remoteAuthToken)
     160             :       throws AccountException, IOException, ConfigInvalidException {
     161           0 :     accountManager.updateLink(
     162           0 :         arsp.getAccountId(),
     163           0 :         authRequestFactory.create(externalIdKeyFactory.create(SCHEME_EXTERNAL, remoteAuthToken)));
     164           0 :   }
     165             : 
     166             :   private void replace(Document doc, String name, String value) {
     167           0 :     Element e = HtmlDomUtil.find(doc, name);
     168           0 :     if (e != null) {
     169           0 :       e.setTextContent(value);
     170             :     } else {
     171           0 :       replaceByClass(doc, name, value);
     172             :     }
     173           0 :   }
     174             : 
     175             :   private void replaceByClass(Node parent, String name, String value) {
     176           0 :     final NodeList list = parent.getChildNodes();
     177           0 :     for (int i = 0; i < list.getLength(); i++) {
     178           0 :       final Node n = list.item(i);
     179           0 :       if (n instanceof Element) {
     180           0 :         final Element e = (Element) n;
     181           0 :         if (name.equals(e.getAttribute("class"))) {
     182           0 :           e.setTextContent(value);
     183             :         }
     184             :       }
     185           0 :       replaceByClass(n, name, value);
     186             :     }
     187           0 :   }
     188             : }

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